1#include"../../cache.h" 2#include"../../json-writer.h" 3#include"lazyload.h" 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 */ 17static intfind_pid(DWORD pid, HANDLE hSnapshot, PROCESSENTRY32 *pe32) 18{ 19 pe32->dwSize =sizeof(PROCESSENTRY32); 20 21if(Process32First(hSnapshot, pe32)) { 22do{ 23if(pe32->th32ProcessID == pid) 24return1; 25}while(Process32Next(hSnapshot, pe32)); 26} 27return0; 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 */ 72static voidget_processes(struct json_writer *jw, HANDLE hSnapshot) 73{ 74 PROCESSENTRY32 pe32; 75 DWORD pid; 76 DWORD pid_list[NR_PIDS_LIMIT]; 77int k, nr_pids =0; 78 79 pid =GetCurrentProcessId(); 80while(find_pid(pid, hSnapshot, &pe32)) { 81/* Only report parents. Omit self from the JSON output. */ 82if(nr_pids) 83jw_array_string(jw, pe32.szExeFile); 84 85/* Check for cycle in snapshot. (Yes, it happened.) */ 86for(k =0; k < nr_pids; k++) 87if(pid == pid_list[k]) { 88jw_array_string(jw,"(cycle)"); 89return; 90} 91 92if(nr_pids == NR_PIDS_LIMIT) { 93jw_array_string(jw,"(truncated)"); 94return; 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 */ 107static voidget_ancestry(void) 108{ 109 HANDLE hSnapshot =CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0); 110 111if(hSnapshot != INVALID_HANDLE_VALUE) { 112struct json_writer jw = JSON_WRITER_INIT; 113 114jw_array_begin(&jw,0); 115get_processes(&jw, hSnapshot); 116jw_end(&jw); 117 118trace2_data_json("process", the_repository,"windows/ancestry", 119&jw); 120 121jw_release(&jw); 122CloseHandle(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 */ 134static voidget_is_being_debugged(void) 135{ 136if(IsDebuggerPresent()) 137trace2_data_intmax("process", the_repository, 138"windows/debugger_present",1); 139} 140 141/* 142 * Emit JSON data with the peak memory usage of the current process. 143 */ 144static voidget_peak_memory_info(void) 145{ 146DECLARE_PROC_ADDR(psapi.dll, BOOL, GetProcessMemoryInfo, HANDLE, 147 PPROCESS_MEMORY_COUNTERS, DWORD); 148 149if(INIT_PROC_ADDR(GetProcessMemoryInfo)) { 150 PROCESS_MEMORY_COUNTERS pmc; 151 152if(GetProcessMemoryInfo(GetCurrentProcess(), &pmc, 153sizeof(pmc))) { 154struct json_writer jw = JSON_WRITER_INIT; 155 156jw_object_begin(&jw,0); 157 158#define KV(kv) #kv, (intmax_t)pmc.kv 159 160jw_object_intmax(&jw,KV(PageFaultCount)); 161jw_object_intmax(&jw,KV(PeakWorkingSetSize)); 162jw_object_intmax(&jw,KV(PeakPagefileUsage)); 163 164jw_end(&jw); 165 166trace2_data_json("process", the_repository, 167"windows/memory", &jw); 168jw_release(&jw); 169} 170} 171} 172 173voidtrace2_collect_process_info(enum trace2_process_info_reason reason) 174{ 175if(!trace2_is_enabled()) 176return; 177 178switch(reason) { 179case TRACE2_PROCESS_INFO_STARTUP: 180get_is_being_debugged(); 181get_ancestry(); 182return; 183 184case TRACE2_PROCESS_INFO_EXIT: 185get_peak_memory_info(); 186return; 187 188default: 189BUG("trace2_collect_process_info: unknown reason '%d'", reason); 190} 191}