]>
Commit | Line | Data |
---|---|---|
353d3d77 JH |
1 | #include "../../cache.h" |
2 | #include "../../json-writer.h" | |
3 | #include <Psapi.h> | |
4 | #include <tlHelp32.h> | |
5 | ||
6 | /* | |
7 | * An arbitrarily chosen value to limit the size of the ancestor | |
8 | * array built in git_processes(). | |
9 | */ | |
10 | #define NR_PIDS_LIMIT 10 | |
11 | ||
12 | /* | |
13 | * Find the process data for the given PID in the given snapshot | |
14 | * and update the PROCESSENTRY32 data. | |
15 | */ | |
16 | static int find_pid(DWORD pid, HANDLE hSnapshot, PROCESSENTRY32 *pe32) | |
17 | { | |
18 | pe32->dwSize = sizeof(PROCESSENTRY32); | |
19 | ||
20 | if (Process32First(hSnapshot, pe32)) { | |
21 | do { | |
22 | if (pe32->th32ProcessID == pid) | |
23 | return 1; | |
24 | } while (Process32Next(hSnapshot, pe32)); | |
25 | } | |
26 | return 0; | |
27 | } | |
28 | ||
29 | /* | |
30 | * Accumulate JSON array of our parent processes: | |
31 | * [ | |
32 | * exe-name-parent, | |
33 | * exe-name-grand-parent, | |
34 | * ... | |
35 | * ] | |
36 | * | |
37 | * Note: we only report the filename of the process executable; the | |
38 | * only way to get its full pathname is to use OpenProcess() | |
39 | * and GetModuleFileNameEx() or QueryfullProcessImageName() | |
40 | * and that seems rather expensive (on top of the cost of | |
41 | * getting the snapshot). | |
42 | * | |
43 | * Note: we compute the set of parent processes by walking the PPID | |
44 | * link in each visited PROCESSENTRY32 record. This search | |
45 | * stops when an ancestor process is not found in the snapshot | |
46 | * (because it exited before the current or intermediate parent | |
47 | * process exited). | |
48 | * | |
49 | * This search may compute an incorrect result if the PPID link | |
50 | * refers to the PID of an exited parent and that PID has been | |
51 | * recycled and given to a new unrelated process. | |
52 | * | |
53 | * Worse, it is possible for a child or descendant of the | |
54 | * current process to be given the recycled PID and cause a | |
55 | * PPID-cycle. This would cause an infinite loop building our | |
56 | * parent process array. | |
57 | * | |
58 | * Note: for completeness, the "System Idle" process has PID=0 and | |
59 | * PPID=0 and could cause another PPID-cycle. We don't expect | |
60 | * Git to be a descendant of the idle process, but because of | |
61 | * PID recycling, it might be possible to get a PPID link value | |
62 | * of 0. This too would cause an infinite loop. | |
63 | * | |
64 | * Therefore, we keep an array of the visited PPIDs to guard against | |
65 | * cycles. | |
66 | * | |
67 | * We use a fixed-size array rather than ALLOC_GROW to keep things | |
68 | * simple and avoid the alloc/realloc overhead. It is OK if we | |
69 | * truncate the search and return a partial answer. | |
70 | */ | |
71 | static void get_processes(struct json_writer *jw, HANDLE hSnapshot) | |
72 | { | |
73 | PROCESSENTRY32 pe32; | |
74 | DWORD pid; | |
75 | DWORD pid_list[NR_PIDS_LIMIT]; | |
76 | int k, nr_pids = 0; | |
77 | ||
78 | pid = GetCurrentProcessId(); | |
79 | while (find_pid(pid, hSnapshot, &pe32)) { | |
80 | /* Only report parents. Omit self from the JSON output. */ | |
81 | if (nr_pids) | |
82 | jw_array_string(jw, pe32.szExeFile); | |
83 | ||
84 | /* Check for cycle in snapshot. (Yes, it happened.) */ | |
85 | for (k = 0; k < nr_pids; k++) | |
86 | if (pid == pid_list[k]) { | |
87 | jw_array_string(jw, "(cycle)"); | |
88 | return; | |
89 | } | |
90 | ||
91 | if (nr_pids == NR_PIDS_LIMIT) { | |
92 | jw_array_string(jw, "(truncated)"); | |
93 | return; | |
94 | } | |
95 | ||
96 | pid_list[nr_pids++] = pid; | |
97 | ||
98 | pid = pe32.th32ParentProcessID; | |
99 | } | |
100 | } | |
101 | ||
102 | /* | |
103 | * Emit JSON data for the current and parent processes. Individual | |
104 | * trace2 targets can decide how to actually print it. | |
105 | */ | |
106 | static void get_ancestry(void) | |
107 | { | |
108 | HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); | |
109 | ||
110 | if (hSnapshot != INVALID_HANDLE_VALUE) { | |
111 | struct json_writer jw = JSON_WRITER_INIT; | |
112 | ||
113 | jw_array_begin(&jw, 0); | |
114 | get_processes(&jw, hSnapshot); | |
115 | jw_end(&jw); | |
116 | ||
117 | trace2_data_json("process", the_repository, "windows/ancestry", | |
118 | &jw); | |
119 | ||
120 | jw_release(&jw); | |
121 | CloseHandle(hSnapshot); | |
122 | } | |
123 | } | |
124 | ||
125 | /* | |
126 | * Is a debugger attached to the current process? | |
127 | * | |
128 | * This will catch debug runs (where the debugger started the process). | |
129 | * This is the normal case. Since this code is called during our startup, | |
130 | * it will not report instances where a debugger is attached dynamically | |
131 | * to a running git process, but that is relatively rare. | |
132 | */ | |
133 | static void get_is_being_debugged(void) | |
134 | { | |
135 | if (IsDebuggerPresent()) | |
136 | trace2_data_intmax("process", the_repository, | |
137 | "windows/debugger_present", 1); | |
138 | } | |
139 | ||
140 | void trace2_collect_process_info(void) | |
141 | { | |
142 | if (!trace2_is_enabled()) | |
143 | return; | |
144 | ||
145 | get_is_being_debugged(); | |
146 | get_ancestry(); | |
147 | } |