Skip to content

Commit ec84196

Browse files
jeffhostetlergitster
authored andcommitted
structured-logging: add child process classification
Teach git to classify child processes as "editor", "pager", "subprocess", "alias", "shell", or "other". Add the child process classification to the child detail events. Mark child processes of class "editor" or "pager" as interactive in the child detail event. Add child summary to cmd_exit event grouping child process by class. Signed-off-by: Jeff Hostetler <[email protected]>
1 parent edff92c commit ec84196

File tree

6 files changed

+125
-0
lines changed

6 files changed

+125
-0
lines changed

editor.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ int launch_editor(const char *path, struct strbuf *buffer, const char *const *en
6666
p.argv = args;
6767
p.env = env;
6868
p.use_shell = 1;
69+
p.slog_child_class = "editor";
6970
if (start_command(&p) < 0)
7071
return error("unable to start editor '%s'", editor);
7172

git.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,7 @@ static int handle_alias(int *argcp, const char ***argv)
328328
commit_pager_choice();
329329

330330
child.use_shell = 1;
331+
child.slog_child_class = "alias";
331332
argv_array_push(&child.args, alias_string + 1);
332333
argv_array_pushv(&child.args, (*argv) + 1);
333334

@@ -651,6 +652,7 @@ static void execv_dashed_external(const char **argv)
651652
cmd.clean_on_exit = 1;
652653
cmd.wait_after_clean = 1;
653654
cmd.silent_exec_failure = 1;
655+
cmd.slog_child_class = "alias";
654656

655657
trace_argv_printf(cmd.args.argv, "trace: exec:");
656658

pager.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ void prepare_pager_args(struct child_process *pager_process, const char *pager)
100100
argv_array_push(&pager_process->args, pager);
101101
pager_process->use_shell = 1;
102102
setup_pager_env(&pager_process->env_array);
103+
pager_process->slog_child_class = "pager";
103104
}
104105

105106
void setup_pager(void)

run-command.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ struct child_process {
1313
struct argv_array env_array;
1414
pid_t pid;
1515
int slog_child_id;
16+
const char *slog_child_class;
1617
/*
1718
* Using .in, .out, .err:
1819
* - Specify 0 for no redirections (child inherits stdin, stdout,

structured-logging.c

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,30 @@ struct aux_data_array {
4949
static struct aux_data_array my__aux_data;
5050
static void format_and_free_aux_data(struct json_writer *jw);
5151

52+
struct child_summary_data {
53+
char *child_class;
54+
uint64_t total_ns;
55+
int count;
56+
};
57+
58+
struct child_summary_data_array {
59+
struct child_summary_data **array;
60+
size_t nr, alloc;
61+
};
62+
63+
static struct child_summary_data_array my__child_summary_data;
64+
static void format_child_summary_data(struct json_writer *jw);
65+
static void free_child_summary_data(void);
66+
5267
struct child_data {
5368
uint64_t start_ns;
5469
uint64_t end_ns;
5570
struct json_writer jw_argv;
71+
char *child_class;
5672
unsigned int is_running:1;
5773
unsigned int is_git_cmd:1;
5874
unsigned int use_shell:1;
75+
unsigned int is_interactive:1;
5976
};
6077

6178
struct child_data_array {
@@ -293,6 +310,12 @@ static void emit_exit_event(void)
293310
format_and_free_aux_data(&jw);
294311
jw_end(&jw);
295312
}
313+
314+
if (my__child_summary_data.nr) {
315+
jw_object_inline_begin_object(&jw, "child_summary");
316+
format_child_summary_data(&jw);
317+
jw_end(&jw);
318+
}
296319
}
297320
jw_end(&jw);
298321

@@ -453,6 +476,7 @@ static void do_final_steps(int in_signal)
453476
argv_array_clear(&my__argv);
454477
jw_release(&my__errors);
455478
strbuf_release(&my__session_id);
479+
free_child_summary_data();
456480
free_timers();
457481
free_children();
458482
}
@@ -835,6 +859,85 @@ static void format_and_free_aux_data(struct json_writer *jw)
835859
my__aux_data.alloc = 0;
836860
}
837861

862+
static struct child_summary_data *find_child_summary_data(
863+
const struct child_data *cd)
864+
{
865+
struct child_summary_data *csd;
866+
char *child_class;
867+
int k;
868+
869+
child_class = cd->child_class;
870+
if (!child_class || !*child_class) {
871+
if (cd->use_shell)
872+
child_class = "shell";
873+
child_class = "other";
874+
}
875+
876+
for (k = 0; k < my__child_summary_data.nr; k++) {
877+
csd = my__child_summary_data.array[k];
878+
if (!strcmp(child_class, csd->child_class))
879+
return csd;
880+
}
881+
882+
csd = xcalloc(1, sizeof(struct child_summary_data));
883+
csd->child_class = xstrdup(child_class);
884+
885+
ALLOC_GROW(my__child_summary_data.array, my__child_summary_data.nr + 1,
886+
my__child_summary_data.alloc);
887+
my__child_summary_data.array[my__child_summary_data.nr++] = csd;
888+
889+
return csd;
890+
}
891+
892+
static void add_child_to_summary_data(const struct child_data *cd)
893+
{
894+
struct child_summary_data *csd = find_child_summary_data(cd);
895+
896+
csd->total_ns += cd->end_ns - cd->start_ns;
897+
csd->count++;
898+
}
899+
900+
static void format_child_summary_data(struct json_writer *jw)
901+
{
902+
int k;
903+
904+
for (k = 0; k < my__child_summary_data.nr; k++) {
905+
struct child_summary_data *csd = my__child_summary_data.array[k];
906+
907+
jw_object_inline_begin_object(jw, csd->child_class);
908+
{
909+
jw_object_intmax(jw, "total_us", csd->total_ns / 1000);
910+
jw_object_intmax(jw, "count", csd->count);
911+
}
912+
jw_end(jw);
913+
}
914+
}
915+
916+
static void free_child_summary_data(void)
917+
{
918+
int k;
919+
920+
for (k = 0; k < my__child_summary_data.nr; k++) {
921+
struct child_summary_data *csd = my__child_summary_data.array[k];
922+
923+
free(csd->child_class);
924+
free(csd);
925+
}
926+
927+
free(my__child_summary_data.array);
928+
}
929+
930+
static unsigned int is_interactive(const char *child_class)
931+
{
932+
if (child_class && *child_class) {
933+
if (!strcmp(child_class, "editor"))
934+
return 1;
935+
if (!strcmp(child_class, "pager"))
936+
return 1;
937+
}
938+
return 0;
939+
}
940+
838941
static struct child_data *alloc_child_data(const struct child_process *cmd)
839942
{
840943
struct child_data *cd = xcalloc(1, sizeof(struct child_data));
@@ -843,6 +946,9 @@ static struct child_data *alloc_child_data(const struct child_process *cmd)
843946
cd->is_running = 1;
844947
cd->is_git_cmd = cmd->git_cmd;
845948
cd->use_shell = cmd->use_shell;
949+
cd->is_interactive = is_interactive(cmd->slog_child_class);
950+
if (cmd->slog_child_class && *cmd->slog_child_class)
951+
cd->child_class = xstrdup(cmd->slog_child_class);
846952

847953
jw_init(&cd->jw_argv);
848954

@@ -895,6 +1001,11 @@ int slog_child_starting(const struct child_process *cmd)
8951001
jw_object_intmax(&jw_data, "child_id", child_id);
8961002
jw_object_bool(&jw_data, "git_cmd", cd->is_git_cmd);
8971003
jw_object_bool(&jw_data, "use_shell", cd->use_shell);
1004+
jw_object_bool(&jw_data, "is_interactive",
1005+
cd->is_interactive);
1006+
if (cd->child_class)
1007+
jw_object_string(&jw_data, "child_class",
1008+
cd->child_class);
8981009
jw_object_sub_jw(&jw_data, "child_argv", &cd->jw_argv);
8991010
}
9001011
jw_end(&jw_data);
@@ -925,6 +1036,8 @@ void slog_child_ended(int child_id, int child_pid, int child_exit_code)
9251036
cd->end_ns = getnanotime();
9261037
cd->is_running = 0;
9271038

1039+
add_child_to_summary_data(cd);
1040+
9281041
/* build data portion for a "detail" event */
9291042
if (slog_want_detail_event("child")) {
9301043
struct json_writer jw_data = JSON_WRITER_INIT;
@@ -934,6 +1047,11 @@ void slog_child_ended(int child_id, int child_pid, int child_exit_code)
9341047
jw_object_intmax(&jw_data, "child_id", child_id);
9351048
jw_object_bool(&jw_data, "git_cmd", cd->is_git_cmd);
9361049
jw_object_bool(&jw_data, "use_shell", cd->use_shell);
1050+
jw_object_bool(&jw_data, "is_interactive",
1051+
cd->is_interactive);
1052+
if (cd->child_class)
1053+
jw_object_string(&jw_data, "child_class",
1054+
cd->child_class);
9371055
jw_object_sub_jw(&jw_data, "child_argv", &cd->jw_argv);
9381056

9391057
jw_object_intmax(&jw_data, "child_pid", child_pid);
@@ -957,6 +1075,7 @@ static void free_children(void)
9571075
struct child_data *cd = my__child_data.array[k];
9581076

9591077
jw_release(&cd->jw_argv);
1078+
free(cd->child_class);
9601079
free(cd);
9611080
}
9621081

sub-process.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ int subprocess_start(struct hashmap *hashmap, struct subprocess_entry *entry, co
8888
process->out = -1;
8989
process->clean_on_exit = 1;
9090
process->clean_on_exit_handler = subprocess_exit_handler;
91+
process->slog_child_class = "subprocess";
9192

9293
err = start_command(process);
9394
if (err) {

0 commit comments

Comments
 (0)