@@ -613,6 +613,46 @@ stopping after the waitpid() and includes OS process creation overhead).
So this time will be slightly larger than the atexit time reported by
the child process itself.
+`"child_ready"`::
+ This event is generated after the current process has started
+ a background process and released all handles to it.
++
+------------
+{
+ "event":"child_ready",
+ ...
+ "child_id":2,
+ "pid":14708, # child PID
+ "ready":"ready", # child ready state
+ "t_rel":0.110605 # observed run-time of child process
+}
+------------
++
+Note that the session-id of the child process is not available to
+the current/spawning process, so the child's PID is reported here as
+a hint for post-processing. (But it is only a hint because the child
+process may be a shell script which doesn't have a session-id.)
++
+This event is generated after the child is started in the background
+and given a little time to boot up and start working. If the child
+startups normally and while the parent is still waiting, the "ready"
+field will have the value "ready".
+If the child is too slow to start and the parent times out, the field
+will have the value "timeout".
+If the child starts but the parent is unable to probe it, the field
+will have the value "error".
++
+After the parent process emits this event, it will release all of its
+handles to the child process and treat the child as a background
+daemon. So even if the child does eventually finish booting up,
+the parent will not emit an updated event.
++
+Note that the `t_rel` field contains the observed run time in seconds
+when the parent released the child process into the background.
+The child is assumed to be a long-running daemon process and may
+outlive the parent process. So the parent's child event times should
+not be compared to the child's atexit times.
+
`"exec"`::
This event is generated before git attempts to `exec()`
another command rather than starting a child process.
@@ -394,6 +394,37 @@ void trace2_child_exit_fl(const char *file, int line, struct child_process *cmd,
us_elapsed_child);
}
+void trace2_child_ready_fl(const char *file, int line,
+ struct child_process *cmd,
+ const char *ready)
+{
+ struct tr2_tgt *tgt_j;
+ int j;
+ uint64_t us_now;
+ uint64_t us_elapsed_absolute;
+ uint64_t us_elapsed_child;
+
+ if (!trace2_enabled)
+ return;
+
+ us_now = getnanotime() / 1000;
+ us_elapsed_absolute = tr2tls_absolute_elapsed(us_now);
+
+ if (cmd->trace2_child_us_start)
+ us_elapsed_child = us_now - cmd->trace2_child_us_start;
+ else
+ us_elapsed_child = 0;
+
+ for_each_wanted_builtin (j, tgt_j)
+ if (tgt_j->pfn_child_ready_fl)
+ tgt_j->pfn_child_ready_fl(file, line,
+ us_elapsed_absolute,
+ cmd->trace2_child_id,
+ cmd->pid,
+ ready,
+ us_elapsed_child);
+}
+
int trace2_exec_fl(const char *file, int line, const char *exe,
const char **argv)
{
@@ -253,6 +253,31 @@ void trace2_child_exit_fl(const char *file, int line, struct child_process *cmd,
#define trace2_child_exit(cmd, code) \
trace2_child_exit_fl(__FILE__, __LINE__, (cmd), (code))
+/**
+ * Emits a "child_ready" message containing the "child-id" and a flag
+ * indicating whether the child was considered "ready" when we
+ * released it.
+ *
+ * This function should be called after starting a daemon process in
+ * the background (and after giving it sufficient time to boot
+ * up) to indicate that we no longer control or own it.
+ *
+ * The "ready" argument should contain one of { "ready", "timeout",
+ * "error" } to indicate the state of the running daemon when we
+ * released it.
+ *
+ * If the daemon process fails to start or it exits or is terminated
+ * while we are still waiting for it, the caller should emit a
+ * regular "child_exit" to report the normal process exit information.
+ *
+ */
+void trace2_child_ready_fl(const char *file, int line,
+ struct child_process *cmd,
+ const char *ready);
+
+#define trace2_child_ready(cmd, ready) \
+ trace2_child_ready_fl(__FILE__, __LINE__, (cmd), (ready))
+
/**
* Emit an 'exec' event prior to calling one of exec(), execv(),
* execvp(), and etc. On Unix-derived systems, this will be the
@@ -45,6 +45,10 @@ typedef void(tr2_tgt_evt_child_exit_fl_t)(const char *file, int line,
uint64_t us_elapsed_absolute, int cid,
int pid, int code,
uint64_t us_elapsed_child);
+typedef void(tr2_tgt_evt_child_ready_fl_t)(const char *file, int line,
+ uint64_t us_elapsed_absolute,
+ int cid, int pid, const char *ready,
+ uint64_t us_elapsed_child);
typedef void(tr2_tgt_evt_thread_start_fl_t)(const char *file, int line,
uint64_t us_elapsed_absolute);
@@ -116,6 +120,7 @@ struct tr2_tgt {
tr2_tgt_evt_alias_fl_t *pfn_alias_fl;
tr2_tgt_evt_child_start_fl_t *pfn_child_start_fl;
tr2_tgt_evt_child_exit_fl_t *pfn_child_exit_fl;
+ tr2_tgt_evt_child_ready_fl_t *pfn_child_ready_fl;
tr2_tgt_evt_thread_start_fl_t *pfn_thread_start_fl;
tr2_tgt_evt_thread_exit_fl_t *pfn_thread_exit_fl;
tr2_tgt_evt_exec_fl_t *pfn_exec_fl;
@@ -383,6 +383,27 @@ static void fn_child_exit_fl(const char *file, int line,
jw_release(&jw);
}
+static void fn_child_ready_fl(const char *file, int line,
+ uint64_t us_elapsed_absolute, int cid, int pid,
+ const char *ready, uint64_t us_elapsed_child)
+{
+ const char *event_name = "child_ready";
+ struct json_writer jw = JSON_WRITER_INIT;
+ double t_rel = (double)us_elapsed_child / 1000000.0;
+
+ jw_object_begin(&jw, 0);
+ event_fmt_prepare(event_name, file, line, NULL, &jw);
+ jw_object_intmax(&jw, "child_id", cid);
+ jw_object_intmax(&jw, "pid", pid);
+ jw_object_string(&jw, "ready", ready);
+ jw_object_double(&jw, "t_rel", 6, t_rel);
+ jw_end(&jw);
+
+ tr2_dst_write_line(&tr2dst_event, &jw.json);
+
+ jw_release(&jw);
+}
+
static void fn_thread_start_fl(const char *file, int line,
uint64_t us_elapsed_absolute)
{
@@ -610,6 +631,7 @@ struct tr2_tgt tr2_tgt_event = {
fn_alias_fl,
fn_child_start_fl,
fn_child_exit_fl,
+ fn_child_ready_fl,
fn_thread_start_fl,
fn_thread_exit_fl,
fn_exec_fl,
@@ -251,6 +251,19 @@ static void fn_child_exit_fl(const char *file, int line,
strbuf_release(&buf_payload);
}
+static void fn_child_ready_fl(const char *file, int line,
+ uint64_t us_elapsed_absolute, int cid, int pid,
+ const char *ready, uint64_t us_elapsed_child)
+{
+ struct strbuf buf_payload = STRBUF_INIT;
+ double elapsed = (double)us_elapsed_child / 1000000.0;
+
+ strbuf_addf(&buf_payload, "child_ready[%d] pid:%d ready:%s elapsed:%.6f",
+ cid, pid, ready, elapsed);
+ normal_io_write_fl(file, line, &buf_payload);
+ strbuf_release(&buf_payload);
+}
+
static void fn_exec_fl(const char *file, int line, uint64_t us_elapsed_absolute,
int exec_id, const char *exe, const char **argv)
{
@@ -330,6 +343,7 @@ struct tr2_tgt tr2_tgt_normal = {
fn_alias_fl,
fn_child_start_fl,
fn_child_exit_fl,
+ fn_child_ready_fl,
NULL, /* thread_start */
NULL, /* thread_exit */
fn_exec_fl,
@@ -360,6 +360,20 @@ static void fn_child_exit_fl(const char *file, int line,
strbuf_release(&buf_payload);
}
+static void fn_child_ready_fl(const char *file, int line,
+ uint64_t us_elapsed_absolute, int cid, int pid,
+ const char *ready, uint64_t us_elapsed_child)
+{
+ const char *event_name = "child_ready";
+ struct strbuf buf_payload = STRBUF_INIT;
+
+ strbuf_addf(&buf_payload, "[ch%d] pid:%d ready:%s", cid, pid, ready);
+
+ perf_io_write_fl(file, line, event_name, NULL, &us_elapsed_absolute,
+ &us_elapsed_child, NULL, &buf_payload);
+ strbuf_release(&buf_payload);
+}
+
static void fn_thread_start_fl(const char *file, int line,
uint64_t us_elapsed_absolute)
{
@@ -553,6 +567,7 @@ struct tr2_tgt tr2_tgt_perf = {
fn_alias_fl,
fn_child_start_fl,
fn_child_exit_fl,
+ fn_child_ready_fl,
fn_thread_start_fl,
fn_thread_exit_fl,
fn_exec_fl,