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 21use strict; 22use warnings; 23use JSON::PP; 24use Data::Dumper; 25use 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 30my$evt_version='1'; 31 32my$show_children=1; 33my$show_exec=1; 34my$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 44my$gen_heredoc=0; 45my$gen_version=''; 46 47GetOptions("children!"=> \$show_children, 48"exec!"=> \$show_exec, 49"threads!"=> \$show_threads, 50"HEREDOC!"=> \$gen_heredoc, 51"VERSION=s"=> \$gen_version) 52or 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_", ...} 64my$sid_map; 65my$sid_count=0; 66 67my$processes; 68 69while(<>) { 70my$line= decode_json($_); 71 72my$sid=""; 73my$sid_sep=""; 74 75my$raw_sid=$line->{'sid'}; 76my@raw_sid_parts=split/\//,$raw_sid; 77foreachmy$raw_sid_k(@raw_sid_parts) { 78if(!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 86my$event=$line->{'event'}; 87 88if($eventeq'version') { 89$processes->{$sid}->{'version'} =$line->{'exe'}; 90if($gen_heredoc==1&&$gen_versioneq$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 101elsif($eventeq'start') { 102$processes->{$sid}->{'argv'} =$line->{'argv'}; 103$processes->{$sid}->{'argv'}[0] ="_EXE_"; 104} 105 106elsif($eventeq'exit') { 107$processes->{$sid}->{'exit_code'} =$line->{'code'}; 108} 109 110elsif($eventeq'atexit') { 111$processes->{$sid}->{'exit_code'} =$line->{'code'}; 112} 113 114elsif($eventeq'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). 118if(exists$line->{'fmt'}) { 119push( @{$processes->{$sid}->{'errors'}},$line->{'fmt'} ); 120} 121elsif(exists$line->{'msg'}) { 122push( @{$processes->{$sid}->{'errors'}},$line->{'msg'} ); 123} 124} 125 126elsif($eventeq'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 136elsif($eventeq'cmd_name') { 137$processes->{$sid}->{'name'} =$line->{'name'}; 138$processes->{$sid}->{'hierarchy'} =$line->{'hierarchy'}; 139} 140 141elsif($eventeq'alias') { 142$processes->{$sid}->{'alias'}->{'key'} =$line->{'alias'}; 143$processes->{$sid}->{'alias'}->{'argv'} =$line->{'argv'}; 144} 145 146elsif($eventeq'def_param') { 147my$kv; 148$kv->{'param'} =$line->{'param'}; 149$kv->{'value'} =$line->{'value'}; 150push( @{$processes->{$sid}->{'params'}},$kv); 151} 152 153elsif($eventeq'child_start') { 154if($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 162elsif($eventeq'child_exit') { 163if($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 170elsif($eventeq'thread_start') { 171if($show_threads==1) { 172} 173} 174 175elsif($eventeq'thread_exit') { 176if($show_threads==1) { 177} 178} 179 180# TODO decide what information we want to test from exec events. 181 182elsif($eventeq'exec') { 183if($show_exec==1) { 184} 185} 186 187elsif($eventeq'exec_result') { 188if($show_exec==1) { 189} 190} 191 192elsif($eventeq'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 198elsif($eventeq'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 214elsif($eventeq'data') { 215my$cat=$line->{'category'}; 216if($cateq'test_category') { 217 218my$key=$line->{'key'}; 219my$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 240my$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 247if($gen_heredoc==1) { 248$out=~s/^/\t\|/gms; 249} 250 251print$out;