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