Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 99 additions & 0 deletions compat/darwin/procinfo.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#define USE_THE_REPOSITORY_VARIABLE

#include "git-compat-util.h"
#include "strbuf.h"
#include "strvec.h"
#include "trace2.h"
#include <sys/sysctl.h>

/*
* An arbitrarily chosen value to limit the depth of the ancestor chain.
*/
#define NR_PIDS_LIMIT 10

/*
* Get the process name and parent PID for a given PID using sysctl().
* Returns 0 on success, -1 on failure.
*/
static int get_proc_info(pid_t pid, struct strbuf *name, pid_t *ppid)
{
int mib[4];
struct kinfo_proc proc;
size_t size = sizeof(proc);

mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PID;
mib[3] = pid;

if (sysctl(mib, 4, &proc, &size, NULL, 0) < 0)
return -1;

if (size == 0)
return -1;

strbuf_addstr(name, proc.kp_proc.p_comm);
*ppid = proc.kp_eproc.e_ppid;

return 0;
}

/*
* Recursively push process names onto the ancestry array.
* We guard against cycles by limiting the depth to NR_PIDS_LIMIT.
*/
static void push_ancestry_name(struct strvec *names, pid_t pid, int depth)
{
struct strbuf name = STRBUF_INIT;
pid_t ppid;

if (depth >= NR_PIDS_LIMIT)
return;

if (pid <= 0)
return;

if (get_proc_info(pid, &name, &ppid) < 0)
goto cleanup;

strvec_push(names, name.buf);

/*
* Recurse to the parent process. Stop if ppid is 0 or 1
* (init/launchd) or if we've reached ourselves (cycle).
*/
if (ppid > 1 && ppid != pid)
push_ancestry_name(names, ppid, depth + 1);

cleanup:
strbuf_release(&name);
}

void trace2_collect_process_info(enum trace2_process_info_reason reason)
{
struct strvec names = STRVEC_INIT;

if (!trace2_is_enabled())
return;

switch (reason) {
case TRACE2_PROCESS_INFO_STARTUP:
push_ancestry_name(&names, getppid(), 0);
if (names.nr)
trace2_cmd_ancestry(names.v);

strvec_clear(&names);
break;

case TRACE2_PROCESS_INFO_EXIT:
/*
* The Windows version of this calls its
* get_peak_memory_info() here. We may want to insert
* similar process-end statistics here in the future.
*/
break;

default:
BUG("trace2_collect_process_info: unknown reason '%d'", reason);
}
}
58 changes: 33 additions & 25 deletions compat/win32/trace2_win32_process_info.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "../../git-compat-util.h"
#include "../../json-writer.h"
#include "../../repository.h"
#include "../../strvec.h"
#include "../../trace2.h"
#include "lazyload.h"
#include <psapi.h>
Expand Down Expand Up @@ -32,12 +33,7 @@ static int find_pid(DWORD pid, HANDLE hSnapshot, PROCESSENTRY32 *pe32)
}

/*
* Accumulate JSON array of our parent processes:
* [
* exe-name-parent,
* exe-name-grand-parent,
* ...
* ]
* Accumulate array of our parent process names.
*
* Note: we only report the filename of the process executable; the
* only way to get its full pathname is to use OpenProcess()
Expand Down Expand Up @@ -73,7 +69,7 @@ static int find_pid(DWORD pid, HANDLE hSnapshot, PROCESSENTRY32 *pe32)
* simple and avoid the alloc/realloc overhead. It is OK if we
* truncate the search and return a partial answer.
*/
static void get_processes(struct json_writer *jw, HANDLE hSnapshot)
static void get_processes(struct strvec *names, HANDLE hSnapshot)
{
PROCESSENTRY32 pe32;
DWORD pid;
Expand All @@ -82,19 +78,19 @@ static void get_processes(struct json_writer *jw, HANDLE hSnapshot)

pid = GetCurrentProcessId();
while (find_pid(pid, hSnapshot, &pe32)) {
/* Only report parents. Omit self from the JSON output. */
/* Only report parents. Omit self from the output. */
if (nr_pids)
jw_array_string(jw, pe32.szExeFile);
strvec_push(names, pe32.szExeFile);

/* Check for cycle in snapshot. (Yes, it happened.) */
for (k = 0; k < nr_pids; k++)
if (pid == pid_list[k]) {
jw_array_string(jw, "(cycle)");
strvec_push(names, "(cycle)");
return;
}

if (nr_pids == NR_PIDS_LIMIT) {
jw_array_string(jw, "(truncated)");
strvec_push(names, "(truncated)");
return;
}

Expand All @@ -105,24 +101,14 @@ static void get_processes(struct json_writer *jw, HANDLE hSnapshot)
}

/*
* Emit JSON data for the current and parent processes. Individual
* trace2 targets can decide how to actually print it.
* Collect the list of parent process names.
*/
static void get_ancestry(void)
static void get_ancestry(struct strvec *names)
{
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

if (hSnapshot != INVALID_HANDLE_VALUE) {
struct json_writer jw = JSON_WRITER_INIT;

jw_array_begin(&jw, 0);
get_processes(&jw, hSnapshot);
jw_end(&jw);

trace2_data_json("process", the_repository, "windows/ancestry",
&jw);

jw_release(&jw);
get_processes(names, hSnapshot);
CloseHandle(hSnapshot);
}
}
Expand Down Expand Up @@ -176,13 +162,35 @@ static void get_peak_memory_info(void)

void trace2_collect_process_info(enum trace2_process_info_reason reason)
{
struct strvec names = STRVEC_INIT;

if (!trace2_is_enabled())
return;

switch (reason) {
case TRACE2_PROCESS_INFO_STARTUP:
get_is_being_debugged();
get_ancestry();
get_ancestry(&names);
if (names.nr) {
/*
Emit the ancestry data as a data_json event to
maintain compatibility for consumers of the older
"windows/ancestry" event.
*/
struct json_writer jw = JSON_WRITER_INIT;
jw_array_begin(&jw, 0);
for (size_t i = 0; i < names.nr; i++)
jw_array_string(&jw, names.v[i]);
jw_end(&jw);
trace2_data_json("process", the_repository,
"windows/ancestry", &jw);
jw_release(&jw);

/* Emit the ancestry data with the new event. */
trace2_cmd_ancestry(names.v);
}

strvec_clear(&names);
return;

case TRACE2_PROCESS_INFO_EXIT:
Expand Down
2 changes: 2 additions & 0 deletions config.mak.uname
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@ ifeq ($(uname_S),Darwin)
HAVE_NS_GET_EXECUTABLE_PATH = YesPlease
CSPRNG_METHOD = arc4random
USE_ENHANCED_BASIC_REGULAR_EXPRESSIONS = YesPlease
HAVE_PLATFORM_PROCINFO = YesPlease
COMPAT_OBJS += compat/darwin/procinfo.o

# Workaround for `gettext` being keg-only and not even being linked via
# `brew link --force gettext`, should be obsolete as of
Expand Down
2 changes: 2 additions & 0 deletions contrib/buildsystems/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,8 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
add_compile_definitions(PROCFS_EXECUTABLE_PATH="/proc/self/exe" HAVE_DEV_TTY )
list(APPEND compat_SOURCES unix-socket.c unix-stream-server.c compat/linux/procinfo.c)
elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
list(APPEND compat_SOURCES compat/darwin/procinfo.c)
endif()

if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
Expand Down
2 changes: 2 additions & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -1292,6 +1292,8 @@ if host_machine.system() == 'linux'
libgit_sources += 'compat/linux/procinfo.c'
elif host_machine.system() == 'windows'
libgit_sources += 'compat/win32/trace2_win32_process_info.c'
elif host_machine.system() == 'darwin'
libgit_sources += 'compat/darwin/procinfo.c'
else
libgit_sources += 'compat/stub/procinfo.c'
endif
Expand Down
Loading