3 # Parse event stream and convert individual events into a summary
4 # record for the process.
6 # Git.exe generates one or more "event" records for each API method,
7 # such as "start <argv>" and "exit <code>", during the life of the git
8 # process. Additionally, the input may contain interleaved events
9 # from multiple concurrent git processes and/or multiple threads from
10 # within a git process.
12 # Accumulate events for each process (based on its unique SID) in a
13 # dictionary and emit process summary records.
15 # Convert some of the variable fields (such as elapsed time) into
16 # placeholders (or omit them) to make HEREDOC comparisons easier in
19 # We may also omit fields not (currently) useful for testing purposes.
27 # The version of the trace2 event target format that we understand.
28 # This is reported in the 'version' event in the 'evt' field.
29 # It comes from the GIT_TRACE2_EVENT_VERSION macro in trace2/tr2_tgt_event.c
30 my $evt_version = '1';
32 my $show_children = 1;
36 # A hack to generate test HEREDOC data for pasting into the test script.
38 # cd "t/trash directory.t0212-trace2-event"
39 # $TT trace ... >trace.event
40 # VV=$(../../git.exe version | sed -e 's/^git version //')
41 # perl ../t0212/parse_events.perl --HEREDOC --VERSION=$VV <trace.event >heredoc
42 # Then paste heredoc into your new test.
47 GetOptions
("children!" => \
$show_children,
48 "exec!" => \
$show_exec,
49 "threads!" => \
$show_threads,
50 "HEREDOC!" => \
$gen_heredoc,
51 "VERSION=s" => \
$gen_version )
52 or die("Error in command line arguments\n");
55 # SIDs contains timestamps and PIDs of the process and its parents.
56 # This makes it difficult to match up in a HEREDOC in the test script.
57 # Build a map from actual SIDs to predictable constant values and yet
58 # keep the parent/child relationships. For example:
59 # {..., "sid":"1539706952458276-8652", ...}
60 # {..., "sid":"1539706952458276-8652/1539706952649493-15452", ...}
62 # {..., "sid":"_SID1_", ...}
63 # {..., "sid":"_SID1_/_SID2_", ...}
70 my $line = decode_json
( $_ );
75 my $raw_sid = $line->{'sid'};
76 my @raw_sid_parts = split /\//, $raw_sid;
77 foreach my $raw_sid_k (@raw_sid_parts) {
78 if (!exists $sid_map->{$raw_sid_k}) {
79 $sid_map->{$raw_sid_k} = '_SID' . $sid_count . '_';
82 $sid = $sid . $sid_sep . $sid_map->{$raw_sid_k};
86 my $event = $line->{'event'};
88 if ($event eq 'version') {
89 $processes->{$sid}->{'version'} = $line->{'exe'};
90 if ($gen_heredoc == 1 && $gen_version eq $line->{'exe'}) {
91 # If we are generating data FOR the test script, replace
92 # the reported git.exe version with a reference to an
93 # environment variable. When our output is pasted into
94 # the test script, it will then be expanded in future
95 # test runs to the THEN current version of git.exe.
96 # We assume that the test script uses env var $V.
97 $processes->{$sid}->{'version'} = "\$V";
101 elsif ($event eq 'start') {
102 $processes->{$sid}->{'argv'} = $line->{'argv'};
103 $processes->{$sid}->{'argv'}[0] = "_EXE_";
106 elsif ($event eq 'exit') {
107 $processes->{$sid}->{'exit_code'} = $line->{'code'};
110 elsif ($event eq 'atexit') {
111 $processes->{$sid}->{'exit_code'} = $line->{'code'};
114 elsif ($event eq 'error') {
115 # For HEREDOC purposes, use the error message format string if
116 # available, rather than the formatted message (which probably
117 # has an absolute pathname).
118 if (exists $line->{'fmt'}) {
119 push( @
{$processes->{$sid}->{'errors'}}, $line->{'fmt'} );
121 elsif (exists $line->{'msg'}) {
122 push( @
{$processes->{$sid}->{'errors'}}, $line->{'msg'} );
126 elsif ($event eq 'cmd_path') {
127 ## $processes->{$sid}->{'path'} = $line->{'path'};
129 # Like in the 'start' event, we need to replace the value of
130 # argv[0] with a token for HEREDOC purposes. However, the
131 # event is only emitted when RUNTIME_PREFIX is defined, so
132 # just omit it for testing purposes.
133 # $processes->{$sid}->{'path'} = "_EXE_";
135 elsif ($event eq 'cmd_ancestry') {
136 # 'cmd_ancestry' is platform-specific and not implemented everywhere, so
137 # just skip it for testing purposes.
139 elsif ($event eq 'cmd_name') {
140 $processes->{$sid}->{'name'} = $line->{'name'};
141 $processes->{$sid}->{'hierarchy'} = $line->{'hierarchy'};
144 elsif ($event eq 'alias') {
145 $processes->{$sid}->{'alias'}->{'key'} = $line->{'alias'};
146 $processes->{$sid}->{'alias'}->{'argv'} = $line->{'argv'};
149 elsif ($event eq 'def_param') {
151 $kv->{'param'} = $line->{'param'};
152 $kv->{'value'} = $line->{'value'};
153 push( @
{$processes->{$sid}->{'params'}}, $kv );
156 elsif ($event eq 'child_start') {
157 if ($show_children == 1) {
158 $processes->{$sid}->{'child'}->{$line->{'child_id'}}->{'child_class'} = $line->{'child_class'};
159 $processes->{$sid}->{'child'}->{$line->{'child_id'}}->{'child_argv'} = $line->{'argv'};
160 $processes->{$sid}->{'child'}->{$line->{'child_id'}}->{'child_argv'}[0] = "_EXE_";
161 $processes->{$sid}->{'child'}->{$line->{'child_id'}}->{'use_shell'} = $line->{'use_shell'} ?
1 : 0;
165 elsif ($event eq 'child_exit') {
166 if ($show_children == 1) {
167 $processes->{$sid}->{'child'}->{$line->{'child_id'}}->{'child_code'} = $line->{'code'};
171 # TODO decide what information we want to test from thread events.
173 elsif ($event eq 'thread_start') {
174 if ($show_threads == 1) {
178 elsif ($event eq 'thread_exit') {
179 if ($show_threads == 1) {
183 # TODO decide what information we want to test from exec events.
185 elsif ($event eq 'exec') {
186 if ($show_exec == 1) {
190 elsif ($event eq 'exec_result') {
191 if ($show_exec == 1) {
195 elsif ($event eq 'def_param') {
196 # Accumulate parameter key/value pairs by key rather than in an array
197 # so that we get overwrite (last one wins) effects.
198 $processes->{$sid}->{'params'}->{$line->{'param'}} = $line->{'value'};
201 elsif ($event eq 'def_repo') {
202 # $processes->{$sid}->{'repos'}->{$line->{'repo'}} = $line->{'worktree'};
203 $processes->{$sid}->{'repos'}->{$line->{'repo'}} = "_WORKTREE_";
206 # A series of potentially nested and threaded region and data events
207 # is fundamentally incompatibile with the type of summary record we
208 # are building in this script. Since they are intended for
209 # perf-trace-like analysis rather than a result summary, we ignore
212 # elsif ($event eq 'region_enter') {
214 # elsif ($event eq 'region_leave') {
217 elsif ($event eq 'data') {
218 my $cat = $line->{'category'};
219 my $key = $line->{'key'};
220 my $value = $line->{'value'};
221 $processes->{$sid}->{'data'}->{$cat}->{$key} = $value;
224 elsif ($event eq 'data_json') {
225 # NEEDSWORK: Ignore due to
226 # compat/win32/trace2_win32_process_info.c, which should log a
227 # "cmd_ancestry" event instead.
231 push @
{$processes->{$sid}->{$event}} => $line->{value
};
234 # This trace2 target does not emit 'printf' events.
236 # elsif ($event eq 'printf') {
240 # Dump the resulting hash into something that we can compare against
241 # in the test script. These options make Dumper output look a little
242 # bit like JSON. Also convert variable references of the form "$VAR*"
243 # so that the matching HEREDOC doesn't need to escape it.
245 $Data::Dumper
::Sortkeys
= 1;
246 $Data::Dumper
::Indent
= 1;
247 $Data::Dumper
::Purity
= 1;
248 $Data::Dumper
::Pair
= ':';
250 my $out = Dumper
($processes);
252 $out =~ s/\$VAR/VAR/g;
254 # Finally, if we're running this script to generate (manually confirmed)
255 # data to add to the test script, guard the indentation.
257 if ($gen_heredoc == 1) {
258 $out =~ s/^/\t\|/gms;