]>
Commit | Line | Data |
---|---|---|
a15860dc JH |
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. | |
e4b75d6a | 29 | # It comes from the GIT_TRACE2_EVENT_VERSION macro in trace2/tr2_tgt_event.c |
a15860dc JH |
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 | ||
136 | elsif ($event eq 'cmd_name') { | |
137 | $processes->{$sid}->{'name'} = $line->{'name'}; | |
138 | $processes->{$sid}->{'hierarchy'} = $line->{'hierarchy'}; | |
139 | } | |
140 | ||
141 | elsif ($event eq 'alias') { | |
142 | $processes->{$sid}->{'alias'}->{'key'} = $line->{'alias'}; | |
143 | $processes->{$sid}->{'alias'}->{'argv'} = $line->{'argv'}; | |
144 | } | |
145 | ||
146 | elsif ($event eq 'def_param') { | |
147 | my $kv; | |
148 | $kv->{'param'} = $line->{'param'}; | |
149 | $kv->{'value'} = $line->{'value'}; | |
150 | push( @{$processes->{$sid}->{'params'}}, $kv ); | |
151 | } | |
152 | ||
153 | elsif ($event eq 'child_start') { | |
154 | if ($show_children == 1) { | |
155 | $processes->{$sid}->{'child'}->{$line->{'child_id'}}->{'child_class'} = $line->{'child_class'}; | |
156 | $processes->{$sid}->{'child'}->{$line->{'child_id'}}->{'child_argv'} = $line->{'argv'}; | |
157 | $processes->{$sid}->{'child'}->{$line->{'child_id'}}->{'child_argv'}[0] = "_EXE_"; | |
158 | $processes->{$sid}->{'child'}->{$line->{'child_id'}}->{'use_shell'} = $line->{'use_shell'} ? 1 : 0; | |
159 | } | |
160 | } | |
161 | ||
162 | elsif ($event eq 'child_exit') { | |
163 | if ($show_children == 1) { | |
164 | $processes->{$sid}->{'child'}->{$line->{'child_id'}}->{'child_code'} = $line->{'code'}; | |
165 | } | |
166 | } | |
167 | ||
168 | # TODO decide what information we want to test from thread events. | |
169 | ||
170 | elsif ($event eq 'thread_start') { | |
171 | if ($show_threads == 1) { | |
172 | } | |
173 | } | |
174 | ||
175 | elsif ($event eq 'thread_exit') { | |
176 | if ($show_threads == 1) { | |
177 | } | |
178 | } | |
179 | ||
180 | # TODO decide what information we want to test from exec events. | |
181 | ||
182 | elsif ($event eq 'exec') { | |
183 | if ($show_exec == 1) { | |
184 | } | |
185 | } | |
186 | ||
187 | elsif ($event eq 'exec_result') { | |
188 | if ($show_exec == 1) { | |
189 | } | |
190 | } | |
191 | ||
192 | elsif ($event eq 'def_param') { | |
193 | # Accumulate parameter key/value pairs by key rather than in an array | |
194 | # so that we get overwrite (last one wins) effects. | |
195 | $processes->{$sid}->{'params'}->{$line->{'param'}} = $line->{'value'}; | |
196 | } | |
197 | ||
198 | elsif ($event eq 'def_repo') { | |
199 | # $processes->{$sid}->{'repos'}->{$line->{'repo'}} = $line->{'worktree'}; | |
200 | $processes->{$sid}->{'repos'}->{$line->{'repo'}} = "_WORKTREE_"; | |
201 | } | |
202 | ||
203 | # A series of potentially nested and threaded region and data events | |
204 | # is fundamentally incompatibile with the type of summary record we | |
205 | # are building in this script. Since they are intended for | |
206 | # perf-trace-like analysis rather than a result summary, we ignore | |
207 | # most of them here. | |
208 | ||
209 | # elsif ($event eq 'region_enter') { | |
210 | # } | |
211 | # elsif ($event eq 'region_leave') { | |
212 | # } | |
213 | ||
214 | elsif ($event eq 'data') { | |
215 | my $cat = $line->{'category'}; | |
216 | if ($cat eq 'test_category') { | |
217 | ||
218 | my $key = $line->{'key'}; | |
219 | my $value = $line->{'value'}; | |
220 | $processes->{$sid}->{'data'}->{$cat}->{$key} = $value; | |
221 | } | |
222 | } | |
223 | ||
224 | # This trace2 target does not emit 'printf' events. | |
225 | # | |
226 | # elsif ($event eq 'printf') { | |
227 | # } | |
228 | } | |
229 | ||
230 | # Dump the resulting hash into something that we can compare against | |
231 | # in the test script. These options make Dumper output look a little | |
232 | # bit like JSON. Also convert variable references of the form "$VAR*" | |
233 | # so that the matching HEREDOC doesn't need to escape it. | |
234 | ||
235 | $Data::Dumper::Sortkeys = 1; | |
236 | $Data::Dumper::Indent = 1; | |
237 | $Data::Dumper::Purity = 1; | |
238 | $Data::Dumper::Pair = ':'; | |
239 | ||
240 | my $out = Dumper($processes); | |
241 | $out =~ s/'/"/g; | |
242 | $out =~ s/\$VAR/VAR/g; | |
243 | ||
244 | # Finally, if we're running this script to generate (manually confirmed) | |
245 | # data to add to the test script, guard the indentation. | |
246 | ||
247 | if ($gen_heredoc == 1) { | |
248 | $out =~ s/^/\t\|/gms; | |
249 | } | |
250 | ||
251 | print $out; |