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