2019-02-22 23:25:10 +01:00
|
|
|
#!/usr/bin/perl
|
|
|
|
#
|
|
|
|
# Parse event stream and convert individual events into a summary
|
|
|
|
# record for the process.
|
|
|
|
#
|
|
|
|
# Git.exe generates one or more "event" records for each API method,
|
|
|
|
# such as "start <argv>" and "exit <code>", during the life of the git
|
|
|
|
# process. Additionally, the input may contain interleaved events
|
|
|
|
# from multiple concurrent git processes and/or multiple threads from
|
|
|
|
# within a git process.
|
|
|
|
#
|
|
|
|
# Accumulate events for each process (based on its unique SID) in a
|
|
|
|
# dictionary and emit process summary records.
|
|
|
|
#
|
|
|
|
# Convert some of the variable fields (such as elapsed time) into
|
|
|
|
# placeholders (or omit them) to make HEREDOC comparisons easier in
|
|
|
|
# the test scripts.
|
|
|
|
#
|
|
|
|
# We may also omit fields not (currently) useful for testing purposes.
|
|
|
|
|
|
|
|
use strict;
|
|
|
|
use warnings;
|
|
|
|
use JSON::PP;
|
|
|
|
use Data::Dumper;
|
|
|
|
use Getopt::Long;
|
|
|
|
|
|
|
|
# The version of the trace2 event target format that we understand.
|
|
|
|
# This is reported in the 'version' event in the 'evt' field.
|
trace2: rename environment variables to GIT_TRACE2*
For an environment variable that is supposed to be set by users, the
GIT_TR2* env vars are just too unclear, inconsistent, and ugly.
Most of the established GIT_* environment variables don't use
abbreviations, and in case of the few that do (GIT_DIR,
GIT_COMMON_DIR, GIT_DIFF_OPTS) it's quite obvious what the
abbreviations (DIR and OPTS) stand for. But what does TR stand for?
Track, traditional, trailer, transaction, transfer, transformation,
transition, translation, transplant, transport, traversal, tree,
trigger, truncate, trust, or ...?!
The trace2 facility, as the '2' suffix in its name suggests, is
supposed to eventually supercede Git's original trace facility. It's
reasonable to expect that the corresponding environment variables
follow suit, and after the original GIT_TRACE variables they are
called GIT_TRACE2; there is no such thing is 'GIT_TR'.
All trace2-specific config variables are, very sensibly, in the
'trace2' section, not in 'tr2'.
OTOH, we don't gain anything at all by omitting the last three
characters of "trace" from the names of these environment variables.
So let's rename all GIT_TR2* environment variables to GIT_TRACE2*,
before they make their way into a stable release.
Signed-off-by: SZEDER Gábor <szeder.dev@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-05-19 16:43:08 +02:00
|
|
|
# It comes from the GIT_TRACE2_EVENT_VERSION macro in trace2/tr2_tgt_event.c
|
2019-02-22 23:25:10 +01:00
|
|
|
my $evt_version = '1';
|
|
|
|
|
|
|
|
my $show_children = 1;
|
|
|
|
my $show_exec = 1;
|
|
|
|
my $show_threads = 1;
|
|
|
|
|
|
|
|
# A hack to generate test HEREDOC data for pasting into the test script.
|
|
|
|
# Usage:
|
|
|
|
# cd "t/trash directory.t0212-trace2-event"
|
|
|
|
# $TT trace ... >trace.event
|
|
|
|
# VV=$(../../git.exe version | sed -e 's/^git version //')
|
|
|
|
# perl ../t0212/parse_events.perl --HEREDOC --VERSION=$VV <trace.event >heredoc
|
|
|
|
# Then paste heredoc into your new test.
|
|
|
|
|
|
|
|
my $gen_heredoc = 0;
|
|
|
|
my $gen_version = '';
|
|
|
|
|
|
|
|
GetOptions("children!" => \$show_children,
|
|
|
|
"exec!" => \$show_exec,
|
|
|
|
"threads!" => \$show_threads,
|
|
|
|
"HEREDOC!" => \$gen_heredoc,
|
|
|
|
"VERSION=s" => \$gen_version )
|
|
|
|
or die("Error in command line arguments\n");
|
|
|
|
|
|
|
|
|
|
|
|
# SIDs contains timestamps and PIDs of the process and its parents.
|
|
|
|
# This makes it difficult to match up in a HEREDOC in the test script.
|
|
|
|
# Build a map from actual SIDs to predictable constant values and yet
|
|
|
|
# keep the parent/child relationships. For example:
|
|
|
|
# {..., "sid":"1539706952458276-8652", ...}
|
|
|
|
# {..., "sid":"1539706952458276-8652/1539706952649493-15452", ...}
|
|
|
|
# becomes:
|
|
|
|
# {..., "sid":"_SID1_", ...}
|
|
|
|
# {..., "sid":"_SID1_/_SID2_", ...}
|
|
|
|
my $sid_map;
|
|
|
|
my $sid_count = 0;
|
|
|
|
|
|
|
|
my $processes;
|
|
|
|
|
|
|
|
while (<>) {
|
|
|
|
my $line = decode_json( $_ );
|
|
|
|
|
|
|
|
my $sid = "";
|
|
|
|
my $sid_sep = "";
|
|
|
|
|
|
|
|
my $raw_sid = $line->{'sid'};
|
|
|
|
my @raw_sid_parts = split /\//, $raw_sid;
|
|
|
|
foreach my $raw_sid_k (@raw_sid_parts) {
|
|
|
|
if (!exists $sid_map->{$raw_sid_k}) {
|
|
|
|
$sid_map->{$raw_sid_k} = '_SID' . $sid_count . '_';
|
|
|
|
$sid_count++;
|
|
|
|
}
|
|
|
|
$sid = $sid . $sid_sep . $sid_map->{$raw_sid_k};
|
|
|
|
$sid_sep = '/';
|
|
|
|
}
|
|
|
|
|
|
|
|
my $event = $line->{'event'};
|
|
|
|
|
|
|
|
if ($event eq 'version') {
|
|
|
|
$processes->{$sid}->{'version'} = $line->{'exe'};
|
|
|
|
if ($gen_heredoc == 1 && $gen_version eq $line->{'exe'}) {
|
|
|
|
# If we are generating data FOR the test script, replace
|
|
|
|
# the reported git.exe version with a reference to an
|
|
|
|
# environment variable. When our output is pasted into
|
|
|
|
# the test script, it will then be expanded in future
|
|
|
|
# test runs to the THEN current version of git.exe.
|
|
|
|
# We assume that the test script uses env var $V.
|
|
|
|
$processes->{$sid}->{'version'} = "\$V";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
elsif ($event eq 'start') {
|
|
|
|
$processes->{$sid}->{'argv'} = $line->{'argv'};
|
|
|
|
$processes->{$sid}->{'argv'}[0] = "_EXE_";
|
|
|
|
}
|
|
|
|
|
|
|
|
elsif ($event eq 'exit') {
|
|
|
|
$processes->{$sid}->{'exit_code'} = $line->{'code'};
|
|
|
|
}
|
|
|
|
|
|
|
|
elsif ($event eq 'atexit') {
|
|
|
|
$processes->{$sid}->{'exit_code'} = $line->{'code'};
|
|
|
|
}
|
|
|
|
|
|
|
|
elsif ($event eq 'error') {
|
|
|
|
# For HEREDOC purposes, use the error message format string if
|
|
|
|
# available, rather than the formatted message (which probably
|
|
|
|
# has an absolute pathname).
|
|
|
|
if (exists $line->{'fmt'}) {
|
|
|
|
push( @{$processes->{$sid}->{'errors'}}, $line->{'fmt'} );
|
|
|
|
}
|
|
|
|
elsif (exists $line->{'msg'}) {
|
|
|
|
push( @{$processes->{$sid}->{'errors'}}, $line->{'msg'} );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
elsif ($event eq 'cmd_path') {
|
|
|
|
## $processes->{$sid}->{'path'} = $line->{'path'};
|
|
|
|
#
|
|
|
|
# Like in the 'start' event, we need to replace the value of
|
|
|
|
# argv[0] with a token for HEREDOC purposes. However, the
|
|
|
|
# event is only emitted when RUNTIME_PREFIX is defined, so
|
|
|
|
# just omit it for testing purposes.
|
|
|
|
# $processes->{$sid}->{'path'} = "_EXE_";
|
|
|
|
}
|
tr2: log parent process name
It can be useful to tell who invoked Git - was it invoked manually by a
user via CLI or script? By an IDE? In some cases - like 'repo' tool -
we can influence the source code and set the GIT_TRACE2_PARENT_SID
environment variable from the caller process. In 'repo''s case, that
parent SID is manipulated to include the string "repo", which means we
can positively identify when Git was invoked by 'repo' tool. However,
identifying parents that way requires both that we know which tools
invoke Git and that we have the ability to modify the source code of
those tools. It cannot scale to keep up with the various IDEs and
wrappers which use Git, most of which we don't know about. Learning
which tools and wrappers invoke Git, and how, would give us insight to
decide where to improve Git's usability and performance.
Unfortunately, there's no cross-platform reliable way to gather the name
of the parent process. If procfs is present, we can use that; otherwise
we will need to discover the name another way. However, the process ID
should be sufficient to look up the process name on most platforms, so
that code may be shareable.
Git for Windows gathers similar information and logs it as a "data_json"
event. However, since "data_json" has a variable format, it is difficult
to parse effectively in some languages; instead, let's pursue a
dedicated "cmd_ancestry" event to record information about the ancestry
of the current process and a consistent, parseable way.
Git for Windows also gathers information about more than one generation
of parent. In Linux further ancestry info can be gathered with procfs,
but it's unwieldy to do so. In the interest of later moving Git for
Windows ancestry logging to the 'cmd_ancestry' event, and in the
interest of later adding more ancestry to the Linux implementation - or
of adding this functionality to other platforms which have an easier
time walking the process tree - let's make 'cmd_ancestry' accept an
array of parentage.
Signed-off-by: Emily Shaffer <emilyshaffer@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-07-22 03:27:07 +02:00
|
|
|
elsif ($event eq 'cmd_ancestry') {
|
|
|
|
# 'cmd_ancestry' is platform-specific and not implemented everywhere, so
|
|
|
|
# just skip it for testing purposes.
|
|
|
|
}
|
2019-02-22 23:25:10 +01:00
|
|
|
elsif ($event eq 'cmd_name') {
|
|
|
|
$processes->{$sid}->{'name'} = $line->{'name'};
|
|
|
|
$processes->{$sid}->{'hierarchy'} = $line->{'hierarchy'};
|
|
|
|
}
|
|
|
|
|
|
|
|
elsif ($event eq 'alias') {
|
|
|
|
$processes->{$sid}->{'alias'}->{'key'} = $line->{'alias'};
|
|
|
|
$processes->{$sid}->{'alias'}->{'argv'} = $line->{'argv'};
|
|
|
|
}
|
|
|
|
|
|
|
|
elsif ($event eq 'def_param') {
|
|
|
|
my $kv;
|
|
|
|
$kv->{'param'} = $line->{'param'};
|
|
|
|
$kv->{'value'} = $line->{'value'};
|
|
|
|
push( @{$processes->{$sid}->{'params'}}, $kv );
|
|
|
|
}
|
|
|
|
|
|
|
|
elsif ($event eq 'child_start') {
|
|
|
|
if ($show_children == 1) {
|
|
|
|
$processes->{$sid}->{'child'}->{$line->{'child_id'}}->{'child_class'} = $line->{'child_class'};
|
|
|
|
$processes->{$sid}->{'child'}->{$line->{'child_id'}}->{'child_argv'} = $line->{'argv'};
|
|
|
|
$processes->{$sid}->{'child'}->{$line->{'child_id'}}->{'child_argv'}[0] = "_EXE_";
|
|
|
|
$processes->{$sid}->{'child'}->{$line->{'child_id'}}->{'use_shell'} = $line->{'use_shell'} ? 1 : 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
elsif ($event eq 'child_exit') {
|
|
|
|
if ($show_children == 1) {
|
|
|
|
$processes->{$sid}->{'child'}->{$line->{'child_id'}}->{'child_code'} = $line->{'code'};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
# TODO decide what information we want to test from thread events.
|
|
|
|
|
|
|
|
elsif ($event eq 'thread_start') {
|
|
|
|
if ($show_threads == 1) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
elsif ($event eq 'thread_exit') {
|
|
|
|
if ($show_threads == 1) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
# TODO decide what information we want to test from exec events.
|
|
|
|
|
|
|
|
elsif ($event eq 'exec') {
|
|
|
|
if ($show_exec == 1) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
elsif ($event eq 'exec_result') {
|
|
|
|
if ($show_exec == 1) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
elsif ($event eq 'def_param') {
|
|
|
|
# Accumulate parameter key/value pairs by key rather than in an array
|
|
|
|
# so that we get overwrite (last one wins) effects.
|
|
|
|
$processes->{$sid}->{'params'}->{$line->{'param'}} = $line->{'value'};
|
|
|
|
}
|
|
|
|
|
|
|
|
elsif ($event eq 'def_repo') {
|
|
|
|
# $processes->{$sid}->{'repos'}->{$line->{'repo'}} = $line->{'worktree'};
|
|
|
|
$processes->{$sid}->{'repos'}->{$line->{'repo'}} = "_WORKTREE_";
|
|
|
|
}
|
|
|
|
|
|
|
|
# A series of potentially nested and threaded region and data events
|
|
|
|
# is fundamentally incompatibile with the type of summary record we
|
|
|
|
# are building in this script. Since they are intended for
|
|
|
|
# perf-trace-like analysis rather than a result summary, we ignore
|
|
|
|
# most of them here.
|
|
|
|
|
|
|
|
# elsif ($event eq 'region_enter') {
|
|
|
|
# }
|
|
|
|
# elsif ($event eq 'region_leave') {
|
|
|
|
# }
|
|
|
|
|
|
|
|
elsif ($event eq 'data') {
|
|
|
|
my $cat = $line->{'category'};
|
trace2: only include "fsync" events if we git_fsync()
Fix the overly verbose trace2 logging added in 9a4987677d3 (trace2:
add stats for fsync operations, 2022-03-30) (first released with
v2.36.0).
Since that change every single "git" command invocation has included
these "data" events, even though we'll only make use of these with
core.fsyncMethod=batch, and even then only have non-zero values if
we're writing object data to disk. See c0f4752ed2f (core.fsyncmethod:
batched disk flushes for loose-objects, 2022-04-04) for that feature.
As we're needing to indent the trace2_data_intmax() lines let's
introduce helper variables to ensure that our resulting lines (which
were already too) don't exceed the recommendations of the
CodingGuidelines. Doing that requires either wrapping them twice, or
introducing short throwaway variable names, let's do the latter.
The result was that e.g. "git version" would previously emit a total
of 6 trace2 events with the GIT_TRACE2_EVENT target (version, start,
cmd_ancestry, cmd_name, exit, atexit), but afterwards would emit
8. We'd emit 2 "data" events before the "exit" event.
The reason we didn't catch this was that the trace2 unit tests added
in a15860dca3f (trace2: t/helper/test-trace2, t0210.sh, t0211.sh,
t0212.sh, 2019-02-22) would omit any "data" events that weren't the
ones it cared about. Before this change to the C code 6/7 of our
"t/t0212-trace2-event.sh" tests would fail if this change was applied
to "t/t0212/parse_events.perl".
Let's make the trace2 testing more strict, and further append any new
events types we don't know about in "t/t0212/parse_events.perl". Since
we only invoke the "test-tool trace2" there's no guarantee that we'll
catch other overly verbose events in the future, but we'll at least
notice if we start emitting new events that are issues every time we
log anything with trace2's JSON target.
We exclude the "data_json" event type, we'd otherwise would fail on
both "win test" and "win+VS test" CI due to the logging added in
353d3d77f4f (trace2: collect Windows-specific process information,
2019-02-22). It looks like that logging should really be using
trace2_cmd_ancestry() instead, which was introduced later in
2f732bf15e6 (tr2: log parent process name, 2021-07-21), but let's
leave it for now.
The fix-up to aaf81223f48 (unpack-objects: use stream_loose_object()
to unpack large objects, 2022-06-11) is needed because we're changing
the behavior of these events as discussed above. Since we'd always
emit a "hardware-flush" event the test added in aaf81223f48 wasn't
testing anything except that this trace2 data was unconditionally
logged. Even if "core.fsyncMethod" wasn't set to "batch" we'd pass the
test.
Now we'll check the expected number of "writeout" v.s. "flush" calls
under "core.fsyncMethod=batch", but note that this doesn't actually
test if we carried out the sync using that method, on a platform where
we'd have to fall back to fsync() each of those "writeout" would
really be a "flush" (i.e. a full fsync()).
But in this case what we're testing is that the logic in
"unpack-objects" behaves as expected, not the OS-specific question of
whether we actually were able to use the "bulk" method.
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-07-18 12:31:52 +02:00
|
|
|
my $key = $line->{'key'};
|
|
|
|
my $value = $line->{'value'};
|
|
|
|
$processes->{$sid}->{'data'}->{$cat}->{$key} = $value;
|
|
|
|
}
|
|
|
|
|
|
|
|
elsif ($event eq 'data_json') {
|
|
|
|
# NEEDSWORK: Ignore due to
|
|
|
|
# compat/win32/trace2_win32_process_info.c, which should log a
|
|
|
|
# "cmd_ancestry" event instead.
|
|
|
|
}
|
|
|
|
|
|
|
|
else {
|
|
|
|
push @{$processes->{$sid}->{$event}} => $line->{value};
|
2019-02-22 23:25:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
# This trace2 target does not emit 'printf' events.
|
|
|
|
#
|
|
|
|
# elsif ($event eq 'printf') {
|
|
|
|
# }
|
|
|
|
}
|
|
|
|
|
|
|
|
# Dump the resulting hash into something that we can compare against
|
|
|
|
# in the test script. These options make Dumper output look a little
|
|
|
|
# bit like JSON. Also convert variable references of the form "$VAR*"
|
|
|
|
# so that the matching HEREDOC doesn't need to escape it.
|
|
|
|
|
|
|
|
$Data::Dumper::Sortkeys = 1;
|
|
|
|
$Data::Dumper::Indent = 1;
|
|
|
|
$Data::Dumper::Purity = 1;
|
|
|
|
$Data::Dumper::Pair = ':';
|
|
|
|
|
|
|
|
my $out = Dumper($processes);
|
|
|
|
$out =~ s/'/"/g;
|
|
|
|
$out =~ s/\$VAR/VAR/g;
|
|
|
|
|
|
|
|
# Finally, if we're running this script to generate (manually confirmed)
|
|
|
|
# data to add to the test script, guard the indentation.
|
|
|
|
|
|
|
|
if ($gen_heredoc == 1) {
|
|
|
|
$out =~ s/^/\t\|/gms;
|
|
|
|
}
|
|
|
|
|
|
|
|
print $out;
|