Merge branch 'ab/config-based-hooks-base' into seen
[git] / t / t0212 / parse_events.perl
1 #!/usr/bin/perl
2 #
3 # Parse event stream and convert individual events into a summary
4 # record for the process.
5 #
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.
11 #
12 # Accumulate events for each process (based on its unique SID) in a
13 # dictionary and emit process summary records.
14 #
15 # Convert some of the variable fields (such as elapsed time) into
16 # placeholders (or omit them) to make HEREDOC comparisons easier in
17 # the test scripts.
18 #
19 # We may also omit fields not (currently) useful for testing purposes.
20
21 use strict;
22 use warnings;
23 use JSON::PP;
24 use Data::Dumper;
25 use Getopt::Long;
26
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';
31
32 my $show_children = 1;
33 my $show_exec     = 1;
34 my $show_threads  = 1;
35
36 # A hack to generate test HEREDOC data for pasting into the test script.
37 # Usage:
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.
43
44 my $gen_heredoc = 0;
45 my $gen_version = '';
46
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");
53
54
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", ...}
61 # becomes:
62 # {..., "sid":"_SID1_", ...}
63 # {..., "sid":"_SID1_/_SID2_", ...}
64 my $sid_map;
65 my $sid_count = 0;
66
67 my $processes;
68
69 while (<>) {
70     my $line = decode_json( $_ );
71
72     my $sid = "";
73     my $sid_sep = "";
74
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 . '_';
80             $sid_count++;
81         }
82         $sid = $sid . $sid_sep . $sid_map->{$raw_sid_k};
83         $sid_sep = '/';
84     }
85     
86     my $event = $line->{'event'};
87
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";
98         }
99     }
100
101     elsif ($event eq 'start') {
102         $processes->{$sid}->{'argv'} = $line->{'argv'};
103         $processes->{$sid}->{'argv'}[0] = "_EXE_";
104     }
105
106     elsif ($event eq 'exit') {
107         $processes->{$sid}->{'exit_code'} = $line->{'code'};
108     }
109
110     elsif ($event eq 'atexit') {
111         $processes->{$sid}->{'exit_code'} = $line->{'code'};
112     }
113
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'} );
120         }
121         elsif (exists $line->{'msg'}) {
122             push( @{$processes->{$sid}->{'errors'}}, $line->{'msg'} );
123         }
124     }
125
126     elsif ($event eq 'cmd_path') {
127         ## $processes->{$sid}->{'path'} = $line->{'path'};
128         #
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_";
134     }
135     elsif ($event eq 'cmd_ancestry') {
136         # 'cmd_ancestry' is platform-specific and not implemented everywhere, so
137         # just skip it for testing purposes.
138     }
139     elsif ($event eq 'cmd_name') {
140         $processes->{$sid}->{'name'} = $line->{'name'};
141         $processes->{$sid}->{'hierarchy'} = $line->{'hierarchy'};
142     }
143
144     elsif ($event eq 'alias') {
145         $processes->{$sid}->{'alias'}->{'key'} = $line->{'alias'};
146         $processes->{$sid}->{'alias'}->{'argv'} = $line->{'argv'};
147     }
148
149     elsif ($event eq 'def_param') {
150         my $kv;
151         $kv->{'param'} = $line->{'param'};
152         $kv->{'value'} = $line->{'value'};
153         push( @{$processes->{$sid}->{'params'}}, $kv );
154     }
155
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;
162         }
163     }
164
165     elsif ($event eq 'child_exit') {
166         if ($show_children == 1) {
167             $processes->{$sid}->{'child'}->{$line->{'child_id'}}->{'child_code'} = $line->{'code'};
168         }
169     }
170
171     # TODO decide what information we want to test from thread events.
172
173     elsif ($event eq 'thread_start') {
174         if ($show_threads == 1) {
175         }
176     }
177
178     elsif ($event eq 'thread_exit') {
179         if ($show_threads == 1) {
180         }
181     }
182
183     # TODO decide what information we want to test from exec events.
184
185     elsif ($event eq 'exec') {
186         if ($show_exec == 1) {
187         }
188     }
189
190     elsif ($event eq 'exec_result') {
191         if ($show_exec == 1) {
192         }
193     }
194
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'};
199     }
200
201     elsif ($event eq 'def_repo') {
202         # $processes->{$sid}->{'repos'}->{$line->{'repo'}} = $line->{'worktree'};
203         $processes->{$sid}->{'repos'}->{$line->{'repo'}} = "_WORKTREE_";
204     }
205
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
210     # most of them here.
211
212     # elsif ($event eq 'region_enter') {
213     # }
214     # elsif ($event eq 'region_leave') {
215     # }
216
217     elsif ($event eq 'data') {
218         my $cat = $line->{'category'};
219         if ($cat eq 'test_category') {
220             
221             my $key = $line->{'key'};
222             my $value = $line->{'value'};
223             $processes->{$sid}->{'data'}->{$cat}->{$key} = $value;
224         }
225     }
226
227     # This trace2 target does not emit 'printf' events.
228     #
229     # elsif ($event eq 'printf') {
230     # }
231 }
232
233 # Dump the resulting hash into something that we can compare against
234 # in the test script.  These options make Dumper output look a little
235 # bit like JSON.  Also convert variable references of the form "$VAR*"
236 # so that the matching HEREDOC doesn't need to escape it.
237
238 $Data::Dumper::Sortkeys = 1;
239 $Data::Dumper::Indent = 1;
240 $Data::Dumper::Purity = 1;
241 $Data::Dumper::Pair = ':';
242
243 my $out = Dumper($processes);
244 $out =~ s/'/"/g;
245 $out =~ s/\$VAR/VAR/g;
246
247 # Finally, if we're running this script to generate (manually confirmed)
248 # data to add to the test script, guard the indentation.
249
250 if ($gen_heredoc == 1) {
251     $out =~ s/^/\t\|/gms;
252 }
253
254 print $out;