merge(third_party/git): Merge squashed git subtree at v2.23.0
Merge commit '1b593e1ea4' as 'third_party/git'
This commit is contained in:
commit
7ef0d62730
3629 changed files with 1139935 additions and 0 deletions
1
third_party/git/compat/win32/alloca.h
vendored
Normal file
1
third_party/git/compat/win32/alloca.h
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
#include <malloc.h>
|
||||
92
third_party/git/compat/win32/dirent.c
vendored
Normal file
92
third_party/git/compat/win32/dirent.c
vendored
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
#include "../../git-compat-util.h"
|
||||
|
||||
struct DIR {
|
||||
struct dirent dd_dir; /* includes d_type */
|
||||
HANDLE dd_handle; /* FindFirstFile handle */
|
||||
int dd_stat; /* 0-based index */
|
||||
};
|
||||
|
||||
static inline void finddata2dirent(struct dirent *ent, WIN32_FIND_DATAW *fdata)
|
||||
{
|
||||
/* convert UTF-16 name to UTF-8 */
|
||||
xwcstoutf(ent->d_name, fdata->cFileName, sizeof(ent->d_name));
|
||||
|
||||
/* Set file type, based on WIN32_FIND_DATA */
|
||||
if (fdata->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
ent->d_type = DT_DIR;
|
||||
else
|
||||
ent->d_type = DT_REG;
|
||||
}
|
||||
|
||||
DIR *opendir(const char *name)
|
||||
{
|
||||
wchar_t pattern[MAX_PATH + 2]; /* + 2 for '/' '*' */
|
||||
WIN32_FIND_DATAW fdata;
|
||||
HANDLE h;
|
||||
int len;
|
||||
DIR *dir;
|
||||
|
||||
/* convert name to UTF-16 and check length < MAX_PATH */
|
||||
if ((len = xutftowcs_path(pattern, name)) < 0)
|
||||
return NULL;
|
||||
|
||||
/* append optional '/' and wildcard '*' */
|
||||
if (len && !is_dir_sep(pattern[len - 1]))
|
||||
pattern[len++] = '/';
|
||||
pattern[len++] = '*';
|
||||
pattern[len] = 0;
|
||||
|
||||
/* open find handle */
|
||||
h = FindFirstFileW(pattern, &fdata);
|
||||
if (h == INVALID_HANDLE_VALUE) {
|
||||
DWORD err = GetLastError();
|
||||
errno = (err == ERROR_DIRECTORY) ? ENOTDIR : err_win_to_posix(err);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* initialize DIR structure and copy first dir entry */
|
||||
dir = xmalloc(sizeof(DIR));
|
||||
dir->dd_handle = h;
|
||||
dir->dd_stat = 0;
|
||||
finddata2dirent(&dir->dd_dir, &fdata);
|
||||
return dir;
|
||||
}
|
||||
|
||||
struct dirent *readdir(DIR *dir)
|
||||
{
|
||||
if (!dir) {
|
||||
errno = EBADF; /* No set_errno for mingw */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* if first entry, dirent has already been set up by opendir */
|
||||
if (dir->dd_stat) {
|
||||
/* get next entry and convert from WIN32_FIND_DATA to dirent */
|
||||
WIN32_FIND_DATAW fdata;
|
||||
if (FindNextFileW(dir->dd_handle, &fdata)) {
|
||||
finddata2dirent(&dir->dd_dir, &fdata);
|
||||
} else {
|
||||
DWORD lasterr = GetLastError();
|
||||
/* POSIX says you shouldn't set errno when readdir can't
|
||||
find any more files; so, if another error we leave it set. */
|
||||
if (lasterr != ERROR_NO_MORE_FILES)
|
||||
errno = err_win_to_posix(lasterr);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
++dir->dd_stat;
|
||||
return &dir->dd_dir;
|
||||
}
|
||||
|
||||
int closedir(DIR *dir)
|
||||
{
|
||||
if (!dir) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
FindClose(dir->dd_handle);
|
||||
free(dir);
|
||||
return 0;
|
||||
}
|
||||
20
third_party/git/compat/win32/dirent.h
vendored
Normal file
20
third_party/git/compat/win32/dirent.h
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
#ifndef DIRENT_H
|
||||
#define DIRENT_H
|
||||
|
||||
typedef struct DIR DIR;
|
||||
|
||||
#define DT_UNKNOWN 0
|
||||
#define DT_DIR 1
|
||||
#define DT_REG 2
|
||||
#define DT_LNK 3
|
||||
|
||||
struct dirent {
|
||||
unsigned char d_type; /* file type to prevent lstat after readdir */
|
||||
char d_name[MAX_PATH * 3]; /* file name (* 3 for UTF-8 conversion) */
|
||||
};
|
||||
|
||||
DIR *opendir(const char *dirname);
|
||||
struct dirent *readdir(DIR *dir);
|
||||
int closedir(DIR *dir);
|
||||
|
||||
#endif /* DIRENT_H */
|
||||
25
third_party/git/compat/win32/git.manifest
vendored
Normal file
25
third_party/git/compat/win32/git.manifest
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<assemblyIdentity type="win32" name="Git" version="0.0.0.1" />
|
||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
||||
<security>
|
||||
<requestedPrivileges>
|
||||
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
|
||||
</requestedPrivileges>
|
||||
</security>
|
||||
</trustInfo>
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<!-- Windows Vista -->
|
||||
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
|
||||
<!-- Windows 7 -->
|
||||
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
|
||||
<!-- Windows 8 -->
|
||||
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
|
||||
<!-- Windows 8.1 -->
|
||||
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
|
||||
<!-- Windows 10 -->
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
|
||||
</application>
|
||||
</compatibility>
|
||||
</assembly>
|
||||
57
third_party/git/compat/win32/lazyload.h
vendored
Normal file
57
third_party/git/compat/win32/lazyload.h
vendored
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
#ifndef LAZYLOAD_H
|
||||
#define LAZYLOAD_H
|
||||
|
||||
/*
|
||||
* A pair of macros to simplify loading of DLL functions. Example:
|
||||
*
|
||||
* DECLARE_PROC_ADDR(kernel32.dll, BOOL, CreateHardLinkW,
|
||||
* LPCWSTR, LPCWSTR, LPSECURITY_ATTRIBUTES);
|
||||
*
|
||||
* if (!INIT_PROC_ADDR(CreateHardLinkW))
|
||||
* return error("Could not find CreateHardLinkW() function";
|
||||
*
|
||||
* if (!CreateHardLinkW(source, target, NULL))
|
||||
* return error("could not create hardlink from %S to %S",
|
||||
* source, target);
|
||||
*/
|
||||
|
||||
struct proc_addr {
|
||||
const char *const dll;
|
||||
const char *const function;
|
||||
FARPROC pfunction;
|
||||
unsigned initialized : 1;
|
||||
};
|
||||
|
||||
/* Declares a function to be loaded dynamically from a DLL. */
|
||||
#define DECLARE_PROC_ADDR(dll, rettype, function, ...) \
|
||||
static struct proc_addr proc_addr_##function = \
|
||||
{ #dll, #function, NULL, 0 }; \
|
||||
static rettype (WINAPI *function)(__VA_ARGS__)
|
||||
|
||||
/*
|
||||
* Loads a function from a DLL (once-only).
|
||||
* Returns non-NULL function pointer on success.
|
||||
* Returns NULL + errno == ENOSYS on failure.
|
||||
* This function is not thread-safe.
|
||||
*/
|
||||
#define INIT_PROC_ADDR(function) \
|
||||
(function = get_proc_addr(&proc_addr_##function))
|
||||
|
||||
static inline void *get_proc_addr(struct proc_addr *proc)
|
||||
{
|
||||
/* only do this once */
|
||||
if (!proc->initialized) {
|
||||
HANDLE hnd;
|
||||
proc->initialized = 1;
|
||||
hnd = LoadLibraryExA(proc->dll, NULL,
|
||||
LOAD_LIBRARY_SEARCH_SYSTEM32);
|
||||
if (hnd)
|
||||
proc->pfunction = GetProcAddress(hnd, proc->function);
|
||||
}
|
||||
/* set ENOSYS if DLL or function was not found */
|
||||
if (!proc->pfunction)
|
||||
errno = ENOSYS;
|
||||
return proc->pfunction;
|
||||
}
|
||||
|
||||
#endif
|
||||
28
third_party/git/compat/win32/path-utils.c
vendored
Normal file
28
third_party/git/compat/win32/path-utils.c
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
#include "../../git-compat-util.h"
|
||||
|
||||
int win32_skip_dos_drive_prefix(char **path)
|
||||
{
|
||||
int ret = has_dos_drive_prefix(*path);
|
||||
*path += ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int win32_offset_1st_component(const char *path)
|
||||
{
|
||||
char *pos = (char *)path;
|
||||
|
||||
/* unc paths */
|
||||
if (!skip_dos_drive_prefix(&pos) &&
|
||||
is_dir_sep(pos[0]) && is_dir_sep(pos[1])) {
|
||||
/* skip server name */
|
||||
pos = strpbrk(pos + 2, "\\/");
|
||||
if (!pos)
|
||||
return 0; /* Error: malformed unc path */
|
||||
|
||||
do {
|
||||
pos++;
|
||||
} while (*pos && !is_dir_sep(*pos));
|
||||
}
|
||||
|
||||
return pos + is_dir_sep(*pos) - path;
|
||||
}
|
||||
20
third_party/git/compat/win32/path-utils.h
vendored
Normal file
20
third_party/git/compat/win32/path-utils.h
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
#define has_dos_drive_prefix(path) \
|
||||
(isalpha(*(path)) && (path)[1] == ':' ? 2 : 0)
|
||||
int win32_skip_dos_drive_prefix(char **path);
|
||||
#define skip_dos_drive_prefix win32_skip_dos_drive_prefix
|
||||
static inline int win32_is_dir_sep(int c)
|
||||
{
|
||||
return c == '/' || c == '\\';
|
||||
}
|
||||
#define is_dir_sep win32_is_dir_sep
|
||||
static inline char *win32_find_last_dir_sep(const char *path)
|
||||
{
|
||||
char *ret = NULL;
|
||||
for (; *path; ++path)
|
||||
if (is_dir_sep(*path))
|
||||
ret = (char *)path;
|
||||
return ret;
|
||||
}
|
||||
#define find_last_dir_sep win32_find_last_dir_sep
|
||||
int win32_offset_1st_component(const char *path);
|
||||
#define offset_1st_component win32_offset_1st_component
|
||||
58
third_party/git/compat/win32/pthread.c
vendored
Normal file
58
third_party/git/compat/win32/pthread.c
vendored
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright (C) 2009 Andrzej K. Haczewski <ahaczewski@gmail.com>
|
||||
*
|
||||
* DISCLAIMER: The implementation is Git-specific, it is subset of original
|
||||
* Pthreads API, without lots of other features that Git doesn't use.
|
||||
* Git also makes sure that the passed arguments are valid, so there's
|
||||
* no need for double-checking.
|
||||
*/
|
||||
|
||||
#include "../../git-compat-util.h"
|
||||
#include "pthread.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
|
||||
static unsigned __stdcall win32_start_routine(void *arg)
|
||||
{
|
||||
pthread_t *thread = arg;
|
||||
thread->tid = GetCurrentThreadId();
|
||||
thread->arg = thread->start_routine(thread->arg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pthread_create(pthread_t *thread, const void *unused,
|
||||
void *(*start_routine)(void*), void *arg)
|
||||
{
|
||||
thread->arg = arg;
|
||||
thread->start_routine = start_routine;
|
||||
thread->handle = (HANDLE)
|
||||
_beginthreadex(NULL, 0, win32_start_routine, thread, 0, NULL);
|
||||
|
||||
if (!thread->handle)
|
||||
return errno;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
int win32_pthread_join(pthread_t *thread, void **value_ptr)
|
||||
{
|
||||
DWORD result = WaitForSingleObject(thread->handle, INFINITE);
|
||||
switch (result) {
|
||||
case WAIT_OBJECT_0:
|
||||
if (value_ptr)
|
||||
*value_ptr = thread->arg;
|
||||
return 0;
|
||||
case WAIT_ABANDONED:
|
||||
return EINVAL;
|
||||
default:
|
||||
return err_win_to_posix(GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
pthread_t pthread_self(void)
|
||||
{
|
||||
pthread_t t = { NULL };
|
||||
t.tid = GetCurrentThreadId();
|
||||
return t;
|
||||
}
|
||||
100
third_party/git/compat/win32/pthread.h
vendored
Normal file
100
third_party/git/compat/win32/pthread.h
vendored
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* Header used to adapt pthread-based POSIX code to Windows API threads.
|
||||
*
|
||||
* Copyright (C) 2009 Andrzej K. Haczewski <ahaczewski@gmail.com>
|
||||
*/
|
||||
|
||||
#ifndef PTHREAD_H
|
||||
#define PTHREAD_H
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
/*
|
||||
* Defines that adapt Windows API threads to pthreads API
|
||||
*/
|
||||
#define pthread_mutex_t CRITICAL_SECTION
|
||||
|
||||
static inline int return_0(int i) {
|
||||
return 0;
|
||||
}
|
||||
#define pthread_mutex_init(a,b) return_0((InitializeCriticalSection((a)), 0))
|
||||
#define pthread_mutex_destroy(a) DeleteCriticalSection((a))
|
||||
#define pthread_mutex_lock EnterCriticalSection
|
||||
#define pthread_mutex_unlock LeaveCriticalSection
|
||||
|
||||
typedef int pthread_mutexattr_t;
|
||||
#define pthread_mutexattr_init(a) (*(a) = 0)
|
||||
#define pthread_mutexattr_destroy(a) do {} while (0)
|
||||
#define pthread_mutexattr_settype(a, t) 0
|
||||
#define PTHREAD_MUTEX_RECURSIVE 0
|
||||
|
||||
#define pthread_cond_t CONDITION_VARIABLE
|
||||
|
||||
#define pthread_cond_init(a,b) InitializeConditionVariable((a))
|
||||
#define pthread_cond_destroy(a) do {} while (0)
|
||||
#define pthread_cond_wait(a,b) return_0(SleepConditionVariableCS((a), (b), INFINITE))
|
||||
#define pthread_cond_signal WakeConditionVariable
|
||||
#define pthread_cond_broadcast WakeAllConditionVariable
|
||||
|
||||
/*
|
||||
* Simple thread creation implementation using pthread API
|
||||
*/
|
||||
typedef struct {
|
||||
HANDLE handle;
|
||||
void *(*start_routine)(void*);
|
||||
void *arg;
|
||||
DWORD tid;
|
||||
} pthread_t;
|
||||
|
||||
extern int pthread_create(pthread_t *thread, const void *unused,
|
||||
void *(*start_routine)(void*), void *arg);
|
||||
|
||||
/*
|
||||
* To avoid the need of copying a struct, we use small macro wrapper to pass
|
||||
* pointer to win32_pthread_join instead.
|
||||
*/
|
||||
#define pthread_join(a, b) win32_pthread_join(&(a), (b))
|
||||
|
||||
extern int win32_pthread_join(pthread_t *thread, void **value_ptr);
|
||||
|
||||
#define pthread_equal(t1, t2) ((t1).tid == (t2).tid)
|
||||
extern pthread_t pthread_self(void);
|
||||
|
||||
static inline void NORETURN pthread_exit(void *ret)
|
||||
{
|
||||
ExitThread((DWORD)(intptr_t)ret);
|
||||
}
|
||||
|
||||
typedef DWORD pthread_key_t;
|
||||
static inline int pthread_key_create(pthread_key_t *keyp, void (*destructor)(void *value))
|
||||
{
|
||||
return (*keyp = TlsAlloc()) == TLS_OUT_OF_INDEXES ? EAGAIN : 0;
|
||||
}
|
||||
|
||||
static inline int pthread_key_delete(pthread_key_t key)
|
||||
{
|
||||
return TlsFree(key) ? 0 : EINVAL;
|
||||
}
|
||||
|
||||
static inline int pthread_setspecific(pthread_key_t key, const void *value)
|
||||
{
|
||||
return TlsSetValue(key, (void *)value) ? 0 : EINVAL;
|
||||
}
|
||||
|
||||
static inline void *pthread_getspecific(pthread_key_t key)
|
||||
{
|
||||
return TlsGetValue(key);
|
||||
}
|
||||
|
||||
#ifndef __MINGW64_VERSION_MAJOR
|
||||
static inline int pthread_sigmask(int how, const sigset_t *set, sigset_t *oset)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* PTHREAD_H */
|
||||
80
third_party/git/compat/win32/syslog.c
vendored
Normal file
80
third_party/git/compat/win32/syslog.c
vendored
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
#include "../../git-compat-util.h"
|
||||
|
||||
static HANDLE ms_eventlog;
|
||||
|
||||
void openlog(const char *ident, int logopt, int facility)
|
||||
{
|
||||
if (ms_eventlog)
|
||||
return;
|
||||
|
||||
ms_eventlog = RegisterEventSourceA(NULL, ident);
|
||||
|
||||
if (!ms_eventlog)
|
||||
warning("RegisterEventSource() failed: %lu", GetLastError());
|
||||
}
|
||||
|
||||
void syslog(int priority, const char *fmt, ...)
|
||||
{
|
||||
WORD logtype;
|
||||
char *str, *pos;
|
||||
int str_len;
|
||||
va_list ap;
|
||||
|
||||
if (!ms_eventlog)
|
||||
return;
|
||||
|
||||
va_start(ap, fmt);
|
||||
str_len = vsnprintf(NULL, 0, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (str_len < 0) {
|
||||
warning_errno("vsnprintf failed");
|
||||
return;
|
||||
}
|
||||
|
||||
str = malloc(st_add(str_len, 1));
|
||||
if (!str) {
|
||||
warning_errno("malloc failed");
|
||||
return;
|
||||
}
|
||||
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(str, str_len + 1, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
while ((pos = strstr(str, "%1")) != NULL) {
|
||||
char *oldstr = str;
|
||||
str = realloc(str, st_add(++str_len, 1));
|
||||
if (!str) {
|
||||
free(oldstr);
|
||||
warning_errno("realloc failed");
|
||||
return;
|
||||
}
|
||||
memmove(pos + 2, pos + 1, strlen(pos));
|
||||
pos[1] = ' ';
|
||||
}
|
||||
|
||||
switch (priority) {
|
||||
case LOG_EMERG:
|
||||
case LOG_ALERT:
|
||||
case LOG_CRIT:
|
||||
case LOG_ERR:
|
||||
logtype = EVENTLOG_ERROR_TYPE;
|
||||
break;
|
||||
|
||||
case LOG_WARNING:
|
||||
logtype = EVENTLOG_WARNING_TYPE;
|
||||
break;
|
||||
|
||||
case LOG_NOTICE:
|
||||
case LOG_INFO:
|
||||
case LOG_DEBUG:
|
||||
default:
|
||||
logtype = EVENTLOG_INFORMATION_TYPE;
|
||||
break;
|
||||
}
|
||||
|
||||
ReportEventA(ms_eventlog, logtype, 0, 0, NULL, 1, 0,
|
||||
(const char **)&str, NULL);
|
||||
free(str);
|
||||
}
|
||||
20
third_party/git/compat/win32/syslog.h
vendored
Normal file
20
third_party/git/compat/win32/syslog.h
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
#ifndef SYSLOG_H
|
||||
#define SYSLOG_H
|
||||
|
||||
#define LOG_PID 0x01
|
||||
|
||||
#define LOG_EMERG 0
|
||||
#define LOG_ALERT 1
|
||||
#define LOG_CRIT 2
|
||||
#define LOG_ERR 3
|
||||
#define LOG_WARNING 4
|
||||
#define LOG_NOTICE 5
|
||||
#define LOG_INFO 6
|
||||
#define LOG_DEBUG 7
|
||||
|
||||
#define LOG_DAEMON (3<<3)
|
||||
|
||||
void openlog(const char *ident, int logopt, int facility);
|
||||
void syslog(int priority, const char *fmt, ...);
|
||||
|
||||
#endif /* SYSLOG_H */
|
||||
191
third_party/git/compat/win32/trace2_win32_process_info.c
vendored
Normal file
191
third_party/git/compat/win32/trace2_win32_process_info.c
vendored
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
#include "../../cache.h"
|
||||
#include "../../json-writer.h"
|
||||
#include "lazyload.h"
|
||||
#include <Psapi.h>
|
||||
#include <tlHelp32.h>
|
||||
|
||||
/*
|
||||
* An arbitrarily chosen value to limit the size of the ancestor
|
||||
* array built in git_processes().
|
||||
*/
|
||||
#define NR_PIDS_LIMIT 10
|
||||
|
||||
/*
|
||||
* Find the process data for the given PID in the given snapshot
|
||||
* and update the PROCESSENTRY32 data.
|
||||
*/
|
||||
static int find_pid(DWORD pid, HANDLE hSnapshot, PROCESSENTRY32 *pe32)
|
||||
{
|
||||
pe32->dwSize = sizeof(PROCESSENTRY32);
|
||||
|
||||
if (Process32First(hSnapshot, pe32)) {
|
||||
do {
|
||||
if (pe32->th32ProcessID == pid)
|
||||
return 1;
|
||||
} while (Process32Next(hSnapshot, pe32));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Accumulate JSON array of our parent processes:
|
||||
* [
|
||||
* exe-name-parent,
|
||||
* exe-name-grand-parent,
|
||||
* ...
|
||||
* ]
|
||||
*
|
||||
* Note: we only report the filename of the process executable; the
|
||||
* only way to get its full pathname is to use OpenProcess()
|
||||
* and GetModuleFileNameEx() or QueryfullProcessImageName()
|
||||
* and that seems rather expensive (on top of the cost of
|
||||
* getting the snapshot).
|
||||
*
|
||||
* Note: we compute the set of parent processes by walking the PPID
|
||||
* link in each visited PROCESSENTRY32 record. This search
|
||||
* stops when an ancestor process is not found in the snapshot
|
||||
* (because it exited before the current or intermediate parent
|
||||
* process exited).
|
||||
*
|
||||
* This search may compute an incorrect result if the PPID link
|
||||
* refers to the PID of an exited parent and that PID has been
|
||||
* recycled and given to a new unrelated process.
|
||||
*
|
||||
* Worse, it is possible for a child or descendant of the
|
||||
* current process to be given the recycled PID and cause a
|
||||
* PPID-cycle. This would cause an infinite loop building our
|
||||
* parent process array.
|
||||
*
|
||||
* Note: for completeness, the "System Idle" process has PID=0 and
|
||||
* PPID=0 and could cause another PPID-cycle. We don't expect
|
||||
* Git to be a descendant of the idle process, but because of
|
||||
* PID recycling, it might be possible to get a PPID link value
|
||||
* of 0. This too would cause an infinite loop.
|
||||
*
|
||||
* Therefore, we keep an array of the visited PPIDs to guard against
|
||||
* cycles.
|
||||
*
|
||||
* We use a fixed-size array rather than ALLOC_GROW to keep things
|
||||
* simple and avoid the alloc/realloc overhead. It is OK if we
|
||||
* truncate the search and return a partial answer.
|
||||
*/
|
||||
static void get_processes(struct json_writer *jw, HANDLE hSnapshot)
|
||||
{
|
||||
PROCESSENTRY32 pe32;
|
||||
DWORD pid;
|
||||
DWORD pid_list[NR_PIDS_LIMIT];
|
||||
int k, nr_pids = 0;
|
||||
|
||||
pid = GetCurrentProcessId();
|
||||
while (find_pid(pid, hSnapshot, &pe32)) {
|
||||
/* Only report parents. Omit self from the JSON output. */
|
||||
if (nr_pids)
|
||||
jw_array_string(jw, pe32.szExeFile);
|
||||
|
||||
/* Check for cycle in snapshot. (Yes, it happened.) */
|
||||
for (k = 0; k < nr_pids; k++)
|
||||
if (pid == pid_list[k]) {
|
||||
jw_array_string(jw, "(cycle)");
|
||||
return;
|
||||
}
|
||||
|
||||
if (nr_pids == NR_PIDS_LIMIT) {
|
||||
jw_array_string(jw, "(truncated)");
|
||||
return;
|
||||
}
|
||||
|
||||
pid_list[nr_pids++] = pid;
|
||||
|
||||
pid = pe32.th32ParentProcessID;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Emit JSON data for the current and parent processes. Individual
|
||||
* trace2 targets can decide how to actually print it.
|
||||
*/
|
||||
static void get_ancestry(void)
|
||||
{
|
||||
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
|
||||
if (hSnapshot != INVALID_HANDLE_VALUE) {
|
||||
struct json_writer jw = JSON_WRITER_INIT;
|
||||
|
||||
jw_array_begin(&jw, 0);
|
||||
get_processes(&jw, hSnapshot);
|
||||
jw_end(&jw);
|
||||
|
||||
trace2_data_json("process", the_repository, "windows/ancestry",
|
||||
&jw);
|
||||
|
||||
jw_release(&jw);
|
||||
CloseHandle(hSnapshot);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Is a debugger attached to the current process?
|
||||
*
|
||||
* This will catch debug runs (where the debugger started the process).
|
||||
* This is the normal case. Since this code is called during our startup,
|
||||
* it will not report instances where a debugger is attached dynamically
|
||||
* to a running git process, but that is relatively rare.
|
||||
*/
|
||||
static void get_is_being_debugged(void)
|
||||
{
|
||||
if (IsDebuggerPresent())
|
||||
trace2_data_intmax("process", the_repository,
|
||||
"windows/debugger_present", 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Emit JSON data with the peak memory usage of the current process.
|
||||
*/
|
||||
static void get_peak_memory_info(void)
|
||||
{
|
||||
DECLARE_PROC_ADDR(psapi.dll, BOOL, GetProcessMemoryInfo, HANDLE,
|
||||
PPROCESS_MEMORY_COUNTERS, DWORD);
|
||||
|
||||
if (INIT_PROC_ADDR(GetProcessMemoryInfo)) {
|
||||
PROCESS_MEMORY_COUNTERS pmc;
|
||||
|
||||
if (GetProcessMemoryInfo(GetCurrentProcess(), &pmc,
|
||||
sizeof(pmc))) {
|
||||
struct json_writer jw = JSON_WRITER_INIT;
|
||||
|
||||
jw_object_begin(&jw, 0);
|
||||
|
||||
#define KV(kv) #kv, (intmax_t)pmc.kv
|
||||
|
||||
jw_object_intmax(&jw, KV(PageFaultCount));
|
||||
jw_object_intmax(&jw, KV(PeakWorkingSetSize));
|
||||
jw_object_intmax(&jw, KV(PeakPagefileUsage));
|
||||
|
||||
jw_end(&jw);
|
||||
|
||||
trace2_data_json("process", the_repository,
|
||||
"windows/memory", &jw);
|
||||
jw_release(&jw);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void trace2_collect_process_info(enum trace2_process_info_reason reason)
|
||||
{
|
||||
if (!trace2_is_enabled())
|
||||
return;
|
||||
|
||||
switch (reason) {
|
||||
case TRACE2_PROCESS_INFO_STARTUP:
|
||||
get_is_being_debugged();
|
||||
get_ancestry();
|
||||
return;
|
||||
|
||||
case TRACE2_PROCESS_INFO_EXIT:
|
||||
get_peak_memory_info();
|
||||
return;
|
||||
|
||||
default:
|
||||
BUG("trace2_collect_process_info: unknown reason '%d'", reason);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue