}
------------
+`"cmd_ancestry"`::
+ This event contains the text command name for the parent (and earlier
+ generations of parents) of the current process, in an array ordered from
+ nearest parent to furthest great-grandparent. It may not be implemented
+ on all platforms.
++
+------------
+{
+ "event":"cmd_ancestry",
+ ...
+ "ancestry":["bash","tmux: server","systemd"]
+}
+------------
+
`"cmd_name"`::
This event contains the command name for this git process
and the hierarchy of commands from parent git processes.
--- /dev/null
+#include "cache.h"
+
+#include "strbuf.h"
+#include "strvec.h"
+#include "trace2.h"
+
+static void get_ancestry_names(struct strvec *names)
+{
+ /*
+ * NEEDSWORK: We could gather the entire pstree into an array to match
+ * functionality with compat/win32/trace2_win32_process_info.c.
+ * To do so, we may want to examine /proc/<pid>/stat. For now, just
+ * gather the immediate parent name which is readily accessible from
+ * /proc/$(getppid())/comm.
+ */
+ struct strbuf procfs_path = STRBUF_INIT;
+ struct strbuf name = STRBUF_INIT;
+
+ /* try to use procfs if it's present. */
+ strbuf_addf(&procfs_path, "/proc/%d/comm", getppid());
+ if (strbuf_read_file(&name, procfs_path.buf, 0)) {
+ strbuf_release(&procfs_path);
+ strbuf_trim_trailing_newline(&name);
+ strvec_push(names, strbuf_detach(&name, NULL));
+ }
+
+ return;
+ /* NEEDSWORK: add non-procfs-linux implementations here */
+}
+
+void trace2_collect_process_info(enum trace2_process_info_reason reason)
+{
+ if (!trace2_is_enabled())
+ return;
+
+ /* someday we may want to write something extra here, but not today */
+ if (reason == TRACE2_PROCESS_INFO_EXIT)
+ return;
+
+ if (reason == TRACE2_PROCESS_INFO_STARTUP) {
+ /*
+ * NEEDSWORK: we could do the entire ptree in an array instead,
+ * see compat/win32/trace2_win32_process_info.c.
+ */
+ struct strvec names = STRVEC_INIT;
+
+ get_ancestry_names(&names);
+
+ if (names.nr)
+ trace2_cmd_ancestry(names.v);
+ strvec_clear(&names);
+ }
+
+ return;
+}
FREAD_READS_DIRECTORIES = UnfortunatelyYes
BASIC_CFLAGS += -DHAVE_SYSINFO
PROCFS_EXECUTABLE_PATH = /proc/self/exe
+ HAVE_PLATFORM_PROCINFO = YesPlease
+ COMPAT_OBJS += compat/linux/procinfo.o
endif
ifeq ($(uname_S),GNU/kFreeBSD)
HAVE_ALLOCA_H = YesPlease
# so just omit it for testing purposes.
# print "cmd_path _EXE_\n";
}
+ elsif ($line =~ m/^cmd_ancestry/) {
+ # 'cmd_ancestry' is not implemented everywhere, so for portability's
+ # sake, skip it when parsing normal.
+ #
+ # print "$line";
+ }
else {
print "$line";
}
# $tokens[$col_rest] = "_EXE_";
goto SKIP_LINE;
}
+ elsif ($tokens[$col_event] =~ m/cmd_ancestry/) {
+ # 'cmd_ancestry' is platform-specific and not implemented everywhere,
+ # so skip it.
+ goto SKIP_LINE;
+ }
elsif ($tokens[$col_event] =~ m/child_exit/) {
$tokens[$col_rest] =~ s/ pid:\d* / pid:_PID_ /;
}
# just omit it for testing purposes.
# $processes->{$sid}->{'path'} = "_EXE_";
}
-
+ elsif ($event eq 'cmd_ancestry') {
+ # 'cmd_ancestry' is platform-specific and not implemented everywhere, so
+ # just skip it for testing purposes.
+ }
elsif ($event eq 'cmd_name') {
$processes->{$sid}->{'name'} = $line->{'name'};
$processes->{$sid}->{'hierarchy'} = $line->{'hierarchy'};
tgt_j->pfn_command_path_fl(file, line, pathname);
}
+void trace2_cmd_ancestry_fl(const char *file, int line, const char **parent_names)
+{
+ struct tr2_tgt *tgt_j;
+ int j;
+
+ if (!trace2_enabled)
+ return;
+
+ for_each_wanted_builtin (j, tgt_j)
+ if (tgt_j->pfn_command_ancestry_fl)
+ tgt_j->pfn_command_ancestry_fl(file, line, parent_names);
+}
+
void trace2_cmd_name_fl(const char *file, int line, const char *name)
{
struct tr2_tgt *tgt_j;
#define trace2_cmd_path(p) trace2_cmd_path_fl(__FILE__, __LINE__, (p))
+/*
+ * Emit an 'ancestry' event with the process name of the current process's
+ * parent process.
+ * This gives post-processors a way to determine what invoked the command and
+ * learn more about usage patterns.
+ */
+void trace2_cmd_ancestry_fl(const char *file, int line, const char **parent_names);
+
+#define trace2_cmd_ancestry(v) trace2_cmd_ancestry_fl(__FILE__, __LINE__, (v))
+
/*
* Emit a 'cmd_name' event with the canonical name of the command.
* This gives post-processors a simple field to identify the command
typedef void(tr2_tgt_evt_command_path_fl_t)(const char *file, int line,
const char *command_path);
+typedef void(tr2_tgt_evt_command_ancestry_fl_t)(const char *file, int line,
+ const char **parent_names);
typedef void(tr2_tgt_evt_command_name_fl_t)(const char *file, int line,
const char *name,
const char *hierarchy);
tr2_tgt_evt_atexit_t *pfn_atexit;
tr2_tgt_evt_error_va_fl_t *pfn_error_va_fl;
tr2_tgt_evt_command_path_fl_t *pfn_command_path_fl;
+ tr2_tgt_evt_command_ancestry_fl_t *pfn_command_ancestry_fl;
tr2_tgt_evt_command_name_fl_t *pfn_command_name_fl;
tr2_tgt_evt_command_mode_fl_t *pfn_command_mode_fl;
tr2_tgt_evt_alias_fl_t *pfn_alias_fl;
jw_release(&jw);
}
+static void fn_command_ancestry_fl(const char *file, int line, const char **parent_names)
+{
+ const char *event_name = "cmd_ancestry";
+ const char *parent_name = NULL;
+ struct json_writer jw = JSON_WRITER_INIT;
+
+ jw_object_begin(&jw, 0);
+ event_fmt_prepare(event_name, file, line, NULL, &jw);
+ jw_object_inline_begin_array(&jw, "ancestry");
+
+ while ((parent_name = *parent_names++))
+ jw_array_string(&jw, parent_name);
+
+ jw_end(&jw); /* 'ancestry' array */
+ jw_end(&jw); /* event object */
+
+ tr2_dst_write_line(&tr2dst_event, &jw.json);
+ jw_release(&jw);
+}
+
static void fn_command_name_fl(const char *file, int line, const char *name,
const char *hierarchy)
{
fn_atexit,
fn_error_va_fl,
fn_command_path_fl,
+ fn_command_ancestry_fl,
fn_command_name_fl,
fn_command_mode_fl,
fn_alias_fl,
strbuf_release(&buf_payload);
}
+static void fn_command_ancestry_fl(const char *file, int line, const char **parent_names)
+{
+ const char *parent_name = NULL;
+ struct strbuf buf_payload = STRBUF_INIT;
+
+ /* cmd_ancestry parent <- grandparent <- great-grandparent */
+ strbuf_addstr(&buf_payload, "cmd_ancestry ");
+ while ((parent_name = *parent_names++)) {
+ strbuf_addstr(&buf_payload, parent_name);
+ /* if we'll write another one after this, add a delimiter */
+ if (parent_names && *parent_names)
+ strbuf_addstr(&buf_payload, " <- ");
+ }
+
+ normal_io_write_fl(file, line, &buf_payload);
+ strbuf_release(&buf_payload);
+}
+
static void fn_command_name_fl(const char *file, int line, const char *name,
const char *hierarchy)
{
fn_atexit,
fn_error_va_fl,
fn_command_path_fl,
+ fn_command_ancestry_fl,
fn_command_name_fl,
fn_command_mode_fl,
fn_alias_fl,
strbuf_release(&buf_payload);
}
+static void fn_command_ancestry_fl(const char *file, int line, const char **parent_names)
+{
+ const char *event_name = "cmd_ancestry";
+ struct strbuf buf_payload = STRBUF_INIT;
+
+ strbuf_addstr(&buf_payload, "ancestry:[");
+ /* It's not an argv but the rules are basically the same. */
+ sq_append_quote_argv_pretty(&buf_payload, parent_names);
+ strbuf_addch(&buf_payload, ']');
+
+ perf_io_write_fl(file, line, event_name, NULL, NULL, NULL, NULL,
+ &buf_payload);
+ strbuf_release(&buf_payload);
+}
+
static void fn_command_name_fl(const char *file, int line, const char *name,
const char *hierarchy)
{
fn_atexit,
fn_error_va_fl,
fn_command_path_fl,
+ fn_command_ancestry_fl,
fn_command_name_fl,
fn_command_mode_fl,
fn_alias_fl,