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 */ 16static intfind_pid(DWORD pid, HANDLE hSnapshot, PROCESSENTRY32 *pe32) 17{ 18 pe32->dwSize =sizeof(PROCESSENTRY32); 19 20if(Process32First(hSnapshot, pe32)) { 21do{ 22if(pe32->th32ProcessID == pid) 23return1; 24}while(Process32Next(hSnapshot, pe32)); 25} 26return0; 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 */ 71static voidget_processes(struct json_writer *jw, HANDLE hSnapshot) 72{ 73 PROCESSENTRY32 pe32; 74 DWORD pid; 75 DWORD pid_list[NR_PIDS_LIMIT]; 76int k, nr_pids =0; 77 78 pid =GetCurrentProcessId(); 79while(find_pid(pid, hSnapshot, &pe32)) { 80/* Only report parents. Omit self from the JSON output. */ 81if(nr_pids) 82jw_array_string(jw, pe32.szExeFile); 83 84/* Check for cycle in snapshot. (Yes, it happened.) */ 85for(k =0; k < nr_pids; k++) 86if(pid == pid_list[k]) { 87jw_array_string(jw,"(cycle)"); 88return; 89} 90 91if(nr_pids == NR_PIDS_LIMIT) { 92jw_array_string(jw,"(truncated)"); 93return; 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 */ 106static voidget_ancestry(void) 107{ 108 HANDLE hSnapshot =CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0); 109 110if(hSnapshot != INVALID_HANDLE_VALUE) { 111struct json_writer jw = JSON_WRITER_INIT; 112 113jw_array_begin(&jw,0); 114get_processes(&jw, hSnapshot); 115jw_end(&jw); 116 117trace2_data_json("process", the_repository,"windows/ancestry", 118&jw); 119 120jw_release(&jw); 121CloseHandle(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 */ 133static voidget_is_being_debugged(void) 134{ 135if(IsDebuggerPresent()) 136trace2_data_intmax("process", the_repository, 137"windows/debugger_present",1); 138} 139 140voidtrace2_collect_process_info(void) 141{ 142if(!trace2_is_enabled()) 143return; 144 145get_is_being_debugged(); 146get_ancestry(); 147}