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
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
|
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE erlref SYSTEM "erlref.dtd">
<erlref>
<header>
<copyright>
<year>1998</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
</legalnotice>
<title>seq_trace</title>
<prepared>[email protected]</prepared>
<docno></docno>
<date>1998-04-16</date>
<rev>A</rev>
</header>
<module>seq_trace</module>
<modulesummary>Sequential Tracing of Messages</modulesummary>
<description>
<p>Sequential tracing makes it possible to trace all messages
resulting from one initial message. Sequential tracing is
completely independent of the ordinary tracing in Erlang, which
is controlled by the <c>erlang:trace/3</c> BIF. See the chapter
<seealso marker="#whatis">What is Sequential Tracing</seealso>
below for more information about what sequential tracing is and
how it can be used.</p>
<p><c>seq_trace</c> provides functions which control all aspects of
sequential tracing. There are functions for activation,
deactivation, inspection and for collection of the trace output.</p>
<note>
<p>The implementation of sequential tracing is in beta status.
This means that the programming interface still might undergo
minor adjustments (possibly incompatible) based on feedback
from users.</p>
</note>
</description>
<datatypes>
<datatype>
<name name="token"/>
<desc>
<p>An opaque term (a tuple) representing a trace token.</p>
</desc>
</datatype>
</datatypes>
<funcs>
<func>
<name name="set_token" arity="1"/>
<fsummary>Set the trace token</fsummary>
<desc>
<p>Sets the trace token for the calling process to <c><anno>Token</anno></c>.
If <c><anno>Token</anno> == []</c> then tracing is disabled, otherwise
<c><anno>Token</anno></c> should be an Erlang term returned from
<c>get_token/0</c> or <c>set_token/1</c>. <c>set_token/1</c>
can be used to temporarily exclude message passing from
the trace by setting the trace token to empty like this:</p>
<code type="none">
OldToken = seq_trace:set_token([]), % set to empty and save
% old value
% do something that should not be part of the trace
io:format("Exclude the signalling caused by this~n"),
seq_trace:set_token(OldToken), % activate the trace token again
... </code>
<p>Returns the previous value of the trace token.</p>
</desc>
</func>
<func>
<name name="set_token" arity="2"/>
<fsummary>Set a component of the trace token</fsummary>
<type name="component"/>
<type name="flag"/>
<type name="value"/>
<desc>
<p>Sets the individual <c><anno>Component</anno></c> of the trace token to
<c><anno>Val</anno></c>. Returns the previous value of the component.</p>
<taglist>
<tag><c>set_token(label, <anno>Integer</anno>)</c></tag>
<item>
<p>The <c>label</c> component is an integer which
identifies all events belonging to the same sequential
trace. If several sequential traces can be active
simultaneously, <c>label</c> is used to identify
the separate traces. Default is 0.</p>
</item>
<tag><c>set_token(serial, SerialValue)</c></tag>
<item>
<p><c>SerialValue = {<anno>Previous</anno>, <anno>Current</anno>}</c>.
The <c>serial</c> component contains counters which
enables the traced messages to be sorted, should never be
set explicitly by the user as these counters are updated
automatically. Default is <c>{0, 0}</c>.</p>
</item>
<tag><c>set_token(send, <anno>Bool</anno>)</c></tag>
<item>
<p>A trace token flag (<c>true | false</c>) which
enables/disables tracing on message sending. Default is
<c>false</c>.</p>
</item>
<tag><c>set_token('receive', <anno>Bool</anno>)</c></tag>
<item>
<p>A trace token flag (<c>true | false</c>) which
enables/disables tracing on message reception. Default is
<c>false</c>.</p>
</item>
<tag><c>set_token(print, <anno>Bool</anno>)</c></tag>
<item>
<p>A trace token flag (<c>true | false</c>) which
enables/disables tracing on explicit calls to
<c>seq_trace:print/1</c>. Default is <c>false</c>.</p>
</item>
<tag><c>set_token(timestamp, <anno>Bool</anno>)</c></tag>
<item>
<p>A trace token flag (<c>true | false</c>) which
enables/disables a timestamp to be generated for each
traced event. Default is <c>false</c>.</p>
</item>
<tag><c>set_token(strict_monotonic_timestamp, <anno>Bool</anno>)</c></tag>
<item>
<p>A trace token flag (<c>true | false</c>) which
enables/disables a strict monotonic timestamp to be generated
for each traced event. Default is <c>false</c>. Timestamps will
consist of
<seealso marker="erts:time_correction#Erlang_Monotonic_Time">Erlang
monotonic time</seealso> and a monotonically increasing
integer. The time-stamp has the same format and value
as produced by <c>{erlang:monotonic_time(nano_seconds),
erlang:unique_integer([monotonic])}</c>.</p>
</item>
<tag><c>set_token(monotonic_timestamp, <anno>Bool</anno>)</c></tag>
<item>
<p>A trace token flag (<c>true | false</c>) which
enables/disables a strict monotonic timestamp to be generated
for each traced event. Default is <c>false</c>. Timestamps
will use
<seealso marker="erts:time_correction#Erlang_Monotonic_Time">Erlang
monotonic time</seealso>. The time-stamp has the same
format and value as produced by
<c>erlang:monotonic_time(nano_seconds)</c>.</p>
</item>
</taglist>
<p>If multiple timestamp flags are passed, <c>timestamp</c> has
precedence over <c>strict_monotonic_timestamp</c> which
in turn has precedence over <c>monotonic_timestamp</c>. All
timestamp flags are remembered, so if two are passed
and the one with highest precedence later is disabled
the other one will become active.</p>
</desc>
</func>
<func>
<name name="get_token" arity="0"/>
<fsummary>Return the value of the trace token</fsummary>
<desc>
<p>Returns the value of the trace token for the calling process.
If <c>[]</c> is returned, it means that tracing is not active.
Any other value returned is the value of an active trace
token. The value returned can be used as input to
the <c>set_token/1</c> function.</p>
</desc>
</func>
<func>
<name name="get_token" arity="1"/>
<fsummary>Return the value of a trace token component</fsummary>
<type name="component"/>
<type name="flag"/>
<type name="value"/>
<desc>
<p>Returns the value of the trace token component
<c>Component</c>. See
<seealso marker="#set_token/2">set_token/2</seealso> for
possible values of <c>Component</c> and <c>Val</c>.</p>
</desc>
</func>
<func>
<name name="print" arity="1"/>
<fsummary>Put the Erlang term <c>TraceInfo</c>into the sequential trace output</fsummary>
<desc>
<p>Puts the Erlang term <c><anno>TraceInfo</anno></c> into the sequential
trace output if the calling process currently is executing
within a sequential trace and the <c>print</c> flag of
the trace token is set.</p>
</desc>
</func>
<func>
<name name="print" arity="2"/>
<fsummary>Put the Erlang term <c>TraceInfo</c>into the sequential trace output</fsummary>
<desc>
<p>Same as <c>print/1</c> with the additional condition that
<c><anno>TraceInfo</anno></c> is output only if <c>Label</c> is equal to
the label component of the trace token.</p>
</desc>
</func>
<func>
<name name="reset_trace" arity="0"/>
<fsummary>Stop all sequential tracing on the local node</fsummary>
<desc>
<p>Sets the trace token to empty for all processes on the
local node. The process internal counters used to create
the serial of the trace token is set to 0. The trace token is
set to empty for all messages in message queues. Together
this will effectively stop all ongoing sequential tracing in
the local node.</p>
</desc>
</func>
<func>
<name name="set_system_tracer" arity="1"/>
<fsummary>Set the system tracer</fsummary>
<type name="tracer"/>
<desc>
<p>Sets the system tracer. The system tracer can be either a
process or port denoted by <c><anno>Tracer</anno></c>. Returns the previous
value (which can be <c>false</c> if no system tracer is
active).</p>
<p>Failure: <c>{badarg, Info}}</c> if <c><anno>Pid</anno></c> is not an
existing local pid.</p>
</desc>
</func>
<func>
<name name="get_system_tracer" arity="0"/>
<fsummary>Return the pid() or port() of the current system tracer.</fsummary>
<type name="tracer"/>
<desc>
<p>Returns the pid or port identifier of the current system
tracer or <c>false</c> if no system tracer is activated.</p>
</desc>
</func>
</funcs>
<section>
<title>Trace Messages Sent To the System Tracer</title>
<p>The format of the messages are:</p>
<code type="none">
{seq_trace, Label, SeqTraceInfo, TimeStamp}</code>
<p>or</p>
<code type="none">
{seq_trace, Label, SeqTraceInfo}</code>
<p>depending on whether the <c>timestamp</c> flag of the trace
token is set to <c>true</c> or <c>false</c>. Where:</p>
<code type="none">
Label = int()
TimeStamp = {Seconds, Milliseconds, Microseconds}
Seconds = Milliseconds = Microseconds = int()</code>
<p>The <c>SeqTraceInfo</c> can have the following formats:</p>
<taglist>
<tag><c>{send, Serial, From, To, Message}</c></tag>
<item>
<p>Used when a process <c>From</c> with its trace token flag
<c>print</c> set to <c>true</c> has sent a message.</p>
</item>
<tag><c>{'receive', Serial, From, To, Message}</c></tag>
<item>
<p>Used when a process <c>To</c> receives a message with a
trace token that has the <c>'receive'</c> flag set to
<c>true</c>.</p>
</item>
<tag><c>{print, Serial, From, _, Info}</c></tag>
<item>
<p>Used when a process <c>From</c> has called
<c>seq_trace:print(Label, TraceInfo)</c> and has a trace
token with the <c>print</c> flag set to <c>true</c> and
<c>label</c> set to <c>Label</c>.</p>
</item>
</taglist>
<p><c>Serial</c> is a tuple <c>{PreviousSerial, ThisSerial}</c>,
where the first integer <c>PreviousSerial</c> denotes the serial
counter passed in the last received message which carried a trace
token. If the process is the first one in a new sequential trace,
<c>PreviousSerial</c> is set to the value of the process internal
"trace clock". The second integer <c>ThisSerial</c> is the serial
counter that a process sets on outgoing messages and it is based
on the process internal "trace clock" which is incremented by one
before it is attached to the trace token in the message.</p>
</section>
<section>
<marker id="whatis"></marker>
<title>What is Sequential Tracing</title>
<p>Sequential tracing is a way to trace a sequence of messages sent
between different local or remote processes, where the sequence
is initiated by one single message. In short it works like this:</p>
<p>Each process has a <em>trace token</em>, which can be empty or
not empty. When not empty the trace token can be seen as
the tuple <c>{Label, Flags, Serial, From}</c>. The trace token is
passed invisibly with each message.</p>
<p>In order to start a sequential trace the user must explicitly set
the trace token in the process that will send the first message
in a sequence.</p>
<p>The trace token of a process is set each time the process
matches a message in a receive statement, according to the trace
token carried by the received message, empty or not.</p>
<p>On each Erlang node a process can be set as the <em>system tracer</em>. This process will receive trace messages each time
a message with a trace token is sent or received (if the trace
token flag <c>send</c> or <c>'receive'</c> is set). The system
tracer can then print each trace event, write it to a file or
whatever suitable.</p>
<note>
<p>The system tracer will only receive those trace events that
occur locally within the Erlang node. To get the whole picture
of a sequential trace that involves processes on several Erlang
nodes, the output from the system tracer on each involved node
must be merged (off line).</p>
</note>
<p>In the following sections Sequential Tracing and its most
fundamental concepts are described.</p>
</section>
<section>
<title>Trace Token</title>
<p>Each process has a current trace token. Initially the token is
empty. When the process sends a message to another process, a
copy of the current token will be sent "invisibly" along with
the message.</p>
<p>The current token of a process is set in two ways, either</p>
<list type="ordered">
<item>
<p>explicitly by the process itself, through a call to
<c>seq_trace:set_token</c>, or</p>
</item>
<item>
<p>when a message is received.</p>
</item>
</list>
<p>In both cases the current token will be set. In particular, if
the token of a message received is empty, the current token of
the process is set to empty.</p>
<p>A trace token contains a label, and a set of flags. Both
the label and the flags are set in 1 and 2 above.</p>
</section>
<section>
<title>Serial</title>
<p>The trace token contains a component which is called
<c>serial</c>. It consists of two integers <c>Previous</c> and
<c>Current</c>. The purpose is to uniquely identify each traced
event within a trace sequence and to order the messages
chronologically and in the different branches if any.</p>
<p>The algorithm for updating <c>Serial</c> can be described as
follows:</p>
<p>Let each process have two counters <c>prev_cnt</c> and
<c>curr_cnt</c> which both are set to 0 when a process is created.
The counters are updated at the following occasions:</p>
<list type="bulleted">
<item>
<p><em>When the process is about to send a message and the trace token is not empty.</em></p>
<p>Let the serial of the trace token be <c>tprev</c> and
<c>tcurr</c>. <br></br>
<c>curr_cnt := curr_cnt + 1</c> <br></br>
<c>tprev := prev_cnt</c> <br></br>
<c>tcurr := curr_cnt</c></p>
<p>The trace token with <c>tprev</c> and <c>tcurr</c> is then
passed along with the message.</p>
</item>
<item>
<p><em>When the process calls</em><c>seq_trace:print(Label, Info)</c>, <em>Label matches the label part of the trace token and the trace token print flag is true.</em></p>
<p>The same algorithm as for send above.</p>
</item>
<item>
<p><em>When a message is received and contains a nonempty trace token.</em></p>
<p>The process trace token is set to the trace token from
the message.</p>
<p>Let the serial of the trace token be <c>tprev</c> and
<c>tcurr</c>. <br></br>
<c><![CDATA[if (curr_cnt < tcurr )]]></c> <br></br>
<c>curr_cnt := tcurr</c> <br></br>
<c>prev_cnt := tcurr</c></p>
</item>
</list>
<p>The <c>curr_cnt</c> of a process is incremented each time
the process is involved in a sequential trace. The counter can
reach its limit (27 bits) if a process is very long-lived and is
involved in much sequential tracing. If the counter overflows it
will not be possible to use the serial for ordering of the trace
events. To prevent the counter from overflowing in the middle of
a sequential trace the function <c>seq_trace:reset_trace/0</c>
can be called to reset the <c>prev_cnt</c> and <c>curr_cnt</c> of
all processes in the Erlang node. This function will also set all
trace tokens in processes and their message queues to empty and
will thus stop all ongoing sequential tracing.</p>
</section>
<section>
<title>Performance considerations</title>
<p>The performance degradation for a system which is enabled for
Sequential Tracing is negligible as long as no tracing is
activated. When tracing is activated there will of course be an
extra cost for each traced message but all other messages will be
unaffected.</p>
</section>
<section>
<title>Ports</title>
<p>Sequential tracing is not performed across ports.</p>
<p>If the user for some reason wants to pass the trace token to a
port this has to be done manually in the code of the port
controlling process. The port controlling processes have to check
the appropriate sequential trace settings (as obtained from
<c>seq_trace:get_token/1</c> and include trace information in
the message data sent to their respective ports.</p>
<p>Similarly, for messages received from a port, a port controller
has to retrieve trace specific information, and set appropriate
sequential trace flags through calls to
<c>seq_trace:set_token/2</c>.</p>
</section>
<section>
<title>Distribution</title>
<p>Sequential tracing between nodes is performed transparently.
This applies to C-nodes built with Erl_Interface too. A C-node
built with Erl_Interface only maintains one trace token, which
means that the C-node will appear as one process from
the sequential tracing point of view.</p>
<p>In order to be able to perform sequential tracing between
distributed Erlang nodes, the distribution protocol has been
extended (in a backward compatible way). An Erlang node which
supports sequential tracing can communicate with an older
(OTP R3B) node but messages passed within that node can of course
not be traced.</p>
</section>
<section>
<title>Example of Usage</title>
<p>The example shown here will give rough idea of how the new
primitives can be used and what kind of output it will produce.</p>
<p>Assume that we have an initiating process with
<c><![CDATA[Pid == <0.30.0>]]></c> like this:</p>
<code type="none">
-module(seqex).
-compile(export_all).
loop(Port) ->
receive
{Port,Message} ->
seq_trace:set_token(label,17),
seq_trace:set_token('receive',true),
seq_trace:set_token(print,true),
seq_trace:print(17,"**** Trace Started ****"),
call_server ! {self(),the_message};
{ack,Ack} ->
ok
end,
loop(Port).</code>
<p>And a registered process <c>call_server</c> with
<c><![CDATA[Pid == <0.31.0>]]></c> like this:</p>
<code type="none">
loop() ->
receive
{PortController,Message} ->
Ack = {received, Message},
seq_trace:print(17,"We are here now"),
PortController ! {ack,Ack}
end,
loop().</code>
<p>A possible output from the system's sequential_tracer (inspired
by AXE-10 and MD-110) could look like:</p>
<pre>
17:<0.30.0> Info {0,1} WITH
"**** Trace Started ****"
17:<0.31.0> Received {0,2} FROM <0.30.0> WITH
{<0.30.0>,the_message}
17:<0.31.0> Info {2,3} WITH
"We are here now"
17:<0.30.0> Received {2,4} FROM <0.31.0> WITH
{ack,{received,the_message}}</pre>
<p>The implementation of a system tracer process that produces
the printout above could look like this:</p>
<code type="none">
tracer() ->
receive
{seq_trace,Label,TraceInfo} ->
print_trace(Label,TraceInfo,false);
{seq_trace,Label,TraceInfo,Ts} ->
print_trace(Label,TraceInfo,Ts);
Other -> ignore
end,
tracer().
print_trace(Label,TraceInfo,false) ->
io:format("~p:",[Label]),
print_trace(TraceInfo);
print_trace(Label,TraceInfo,Ts) ->
io:format("~p ~p:",[Label,Ts]),
print_trace(TraceInfo).
print_trace({print,Serial,From,_,Info}) ->
io:format("~p Info ~p WITH~n~p~n", [From,Serial,Info]);
print_trace({'receive',Serial,From,To,Message}) ->
io:format("~p Received ~p FROM ~p WITH~n~p~n",
[To,Serial,From,Message]);
print_trace({send,Serial,From,To,Message}) ->
io:format("~p Sent ~p TO ~p WITH~n~p~n",
[From,Serial,To,Message]).</code>
<p>The code that creates a process that runs the tracer function
above and sets that process as the system tracer could look like
this:</p>
<code type="none">
start() ->
Pid = spawn(?MODULE,tracer,[]),
seq_trace:set_system_tracer(Pid), % set Pid as the system tracer
ok.</code>
<p>With a function like <c>test/0</c> below the whole example can be
started.</p>
<code type="none">
test() ->
P = spawn(?MODULE, loop, [port]),
register(call_server, spawn(?MODULE, loop, [])),
start(),
P ! {port,message}.</code>
</section>
</erlref>
|