-
Notifications
You must be signed in to change notification settings - Fork 0
/
README
197 lines (154 loc) · 7.93 KB
/
README
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
Intro
-----
This is Pip, a system for finding bugs in distributed systems using
expectations. It was written by Patrick Reynolds from 2005-2007 and is
copyrighted by Duke University (2005-2006) and Patrick Reynolds (2007).
See the file "COPYING" for details.
Email: patrick at piki dot org
Building
--------
Type ./configure. Then type make. If it doesn't work, send me email with
the error output and the name and version of your Linux distribution.
If you will be using MySQL support, create a ~/.piprc file to describe
where your database is:
db.host = HOSTNAME # optional: default=localhost
db.name = DATABASE_NAME # optional: default="pip"
db.user = DATABASE_USER # optional: default=<your login>
db.password = PASSWORD # optional: default=<none>
Requirements:
Linux
libglade-2.0, GTK+-2.4 or higher, libxml2
Perl
libpcre
Bison (GNU yacc clone) and Lex
Recommended:
librsvg2 (for communication-graph support in pathview)
Optional:
Java SDK
libmysqlclient
Running it
----------
1) Put some annotations into your application. If you are using Mace,
many but not all of the annotations will be done for you.
2) Run your application on one or more hosts.
3) Gather the files /tmp/trace-* from all hosts to a single place. scp is
good for this. You should probably delete /tmp/trace-* from the hosts in
your testbed in case you run the application again.
4) Import the trace files. You can load them to a single flat file:
./dbfill/new-reconcile trace-file-name trace-*
... or to a MySQL database:
./dbfill/dbfill trace-table-name trace-*
5) Write some expectations
or 5b) Generate expectations automatically:
./expectations/makeexp <name> <pathid> > <name>.pip
or 5c) Browse your trace without any expectations:
cd pathview && ./pathview <name> /dev/null
Annotations
-----------
The files in pip/libannotate/tests are all (very simple) C programs with
annotations in them.
First, one optimization. Full thread support is slow, about 10
microseconds per task or path annotation. (Notices and messages are
faster and are not affected by thread support.) There are three ways to
make annotations faster:
- disable threads in the build. Comment out -DTHREADS and -lpthread in
libannotate/Makefile
- use a Linux kernel before 2.6.6
- patch the kernel on each target with one of the rusage patches. Kernels
from 2.6.18+ can use rusage-diff-2.6.20. Kernels before that may try
rusage-diff-2.6.12, but if it applies with any "fuzz," you probably
shouldn't run it.
If you take any of these actions, annotations cost 1 microsecond instead of 10.
Annotations mark three different behaviors in your program: tasks,
notices, and messages.
- A task is any chunk of processing, generally a millisecond or longer.
Pip will assign it a name and keep resource usage statistics.
- A notice is essentially a log statement, in the context of a thread,
path, and (sometimes) task.
- A message is any communication, including network messages, IPC, timer
events (set=send, fire=receive) or even locks (release=send,
acquire=receive).
Application behavior must be divided into paths, generally in response to
an outside event like a user request. Paths are programmer-defined. Each
thread is in exactly one path at any given time; all annotations are
associated with the current path. If a path spans multiple nodes, all
nodes except the first must "receive" it through a message annotation.
Path IDs should be unique across all nodes, across the entire duration of
a trace. Sufficiently large random numbers (>= 128 bits) will probably be
fine, as will a unique node identifier (IP address in some cases) plus a
serial number. Reusing path IDs in subsequent traces is fine.
A few general hints about applying annotations. You'll probably have to
add two fields to most messages, to convey path ID and message ID. When
you receive a message, remember to set the path ID before you call receive
message or start task. Tasks can be nested, but remember to end the child
task before ending the parent one.
The annotations are as listed below. Note that roles and level aren't
really supported anywhere, so I invariably use NULL and 0, respectively.
Roles and levels might be deprecated.
ANNOTATE_INIT()
Initialize annotation state, including a trace file. Call this once per
host, e.g., in main() or a global constructor.
ANNOTATE_SET_PATH_ID(char *roles, int level, void *id, int size)
ANNOTATE_SET_PATH_ID_INT(char *roles, int level, int id)
ANNOTATE_SET_PATH_ID_STR(char *roles, int level, char *fmt, ...)
Set the current path ID for the current thread. Call this to associate
subsequent behavior with a specific path. The general form takes a memory
chunk and size; the _INT form is a shortcut for 32-bit ints; the _STR form
is a shortcut for printf-style format strings.
ANNOTATE_PUSH_PATH_ID(char *roles, int level, void *id, int size)
ANNOTATE_PUSH_PATH_ID_INT(char *roles, int level, int id)
ANNOTATE_PUSH_PATH_ID_STR(char *roles, int level, char *fmt, ...)
ANNOTATE_POP_PATH_ID(char *roles, int level)
Push a path ID onto a stack (max stack size is 10). Same three variants
as above.
Pop it back off.
const void *ANNOTATE_GET_PATH_ID(int *len)
Get the current path ID. On return, len contains the size of the ID.
ANNOTATE_END_PATH_ID(char *roles, int level, void *id, int size)
ANNOTATE_END_PATH_ID_INT(char *roles, int level, int id)
ANNOTATE_END_PATH_ID_STR(char *roles, int level, char *fmt, ...)
Indicate that the given path is finished on this thread. Call it once per
thread. Once all threads have called ANNOTATE_END_PATH_ID, the path is
available for processing.
NB: this is useful for real-time path checking, which is not actually
written. For now, it's a no-op. It's always optional.
ANNOTATE_START_TASK(char *roles, int level, char *name)
ANNOTATE_END_TASK(char *roles, int level, char *name)
Start and end a task, by name. The beginning and end for a task must be
in the same host, thread, and path.
ANNOTATE_NOTICE(char *roles, int level, char *fmt, ...)
Indicates a log message. The format argument behaves like printf.
ANNOTATE_SEND(char *roles, int level, char *msgid, int idsz, int msgsz)
ANNOTATE_SEND_INT(char *roles, int level, char *msgid, int id)
ANNOTATE_SEND_STR(char *roles, int level, char *msgid, char *fmt, ...)
ANNOTATE_RECEIVE(char *roles, int level, char *msgid, int idsz, int msgsz)
ANNOTATE_RECEIVE_INT(char *roles, int level, char *msgid, int id)
ANNOTATE_RECEIVE_STR(char *roles, int level, char *msgid, char *fmt, ...)
Indicates communication sent or received, including network messages, IPC,
timers, or locks. The send and receive events must have the same ID and
size. Any given ID can be used only once per trace (system-wide). A
message ID may be the same as a path ID, as long as it isn't the same as
any other message ID.
Expectations
------------
Expectations are patterns of behavior that are specifically allowed or
disallowed. Expectations can be composed, and a special category of
expectations called "aggregates" applies to the behavior of an entire
trace.
Expectations can be validators, invalidators, or recognizers. Validators
and invalidators classify behavior as follows: behavior that matches one
or more validators and zero invalidators is considered "expected"
behavior. Anything else (i.e., matching any invalidators or zero
validators) is "unexpected" and will generate a warning. Recognizers are
neutral and are used as building blocks for other expectations.
Both "pathcheck" and "pathview" check paths against validators and
invalidators. Only "pathcheck" checks aggregates.
There are several example .pip files in the expectations directory that
illustrate all the features of the language. You can generate
expectations for one path in your trace automatically by running:
makeexp {file|table} path_id
You can also visualize paths without first writing any expectations by
running:
pathview {file|table} /dev/null
There is more information about expectations in the NSDI 2006 paper, which
is available at http://issg.cs.duke.edu/pip/