From e7ddf084420d42e42609a11101e4e214fa2019d0 Mon Sep 17 00:00:00 2001 From: Mario Hewardt Date: Tue, 28 May 2024 08:51:51 -0700 Subject: [PATCH] Includes improvements related to containerized workflows. (#250) * Updates * Updates * Updates * Updates * Updates * Updates * Address PR feedback --- CMakeLists.txt | 6 - INSTALL.md | 21 +- README.md | 12 +- dist/changelog | 3 + ebpf/procdump_ebpf.c | 238 +++++++++++++----- ebpf/procdump_ebpf.h | 24 +- include/CoreDumpWriter.h | 3 +- include/Logging.h | 2 +- include/ProcDumpConfiguration.h | 11 +- include/ProcDumpVersion.h.in | 2 +- include/Process.h | 8 +- include/Restrack.h | 2 +- procdump.1 | 10 +- src/CoreDumpWriter.cpp | 44 +++- src/Logging.cpp | 25 +- src/Monitor.cpp | 149 ++++++++--- src/ProcDumpConfiguration.cpp | 43 +++- src/Procdump.cpp | 23 +- src/Process.cpp | 87 ++++++- src/Restrack.cpp | 38 ++- tests/integration/run.sh | 10 +- tests/integration/runProcDumpAndValidate.sh | 8 +- .../dotnet_0ExceptionDump1Thrown_dump.sh | 2 +- ...tnet_0WildcardExceptionDump1Thrown_dump.sh | 2 +- ...net_1FilterExceptionMsgDump1Thrown_dump.sh | 2 +- ...tnet_1WildcardExceptionDump1Thrown_dump.sh | 2 +- ...dcardFilterExceptionMsgDump1Thrown_dump.sh | 2 +- .../scenarios/dotnet_1_gc_gen_1_dump.sh | 2 +- .../scenarios/dotnet_1_gc_threshold_1_dump.sh | 2 +- .../dotnet_1exceptionDump1Thrown_dump.sh | 2 +- .../dotnet_1exceptionDump2Thrown_dump.sh | 2 +- ...tnet_2WildcardExceptionDump2Thrown_dump.sh | 2 +- .../dotnet_2exceptionDump2Thrown_dump.sh | 2 +- .../dotnet_3_gc_thresholds_3_dumps.sh | 2 +- .../dotnet_LOH_thresholds_3_dumps.sh | 2 +- .../scenarios/dotnet_MemMultipleDumps.sh | 2 +- .../dotnet_POH_thresholds_3_dumps.sh | 2 +- .../scenarios/dotnet_exception_notdump.sh | 2 +- .../dotnet_gen_2_thresholds_3_dumps.sh | 2 +- .../scenarios/dotnet_sigint_procdump.sh | 2 +- .../dotnet_wildcardexception_notdump.sh | 2 +- 41 files changed, 602 insertions(+), 205 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0fdb921..8b3628f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,12 +39,6 @@ endif() set(PROJECT_VERSION_TWEAK 0) file(READ "dist/changelog" CHANGE_LOG) -# -# enable Debug while pre-release; disable Debug in post-release -# -#set(CMAKE_BUILD_TYPE Debug) -#option(DEBUG_K "Enter debug mode" On) - # # package name # diff --git a/INSTALL.md b/INSTALL.md index 5f769c3..85dbcfb 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -5,7 +5,7 @@ sudo yum install procdump ``` -## Ubuntu 20.04, 22.04, 23.04 +## Ubuntu 20.04, 22.04, 24.04 #### 1. Register Microsoft key and feed ```sh wget -q https://packages.microsoft.com/config/ubuntu/$(lsb_release -rs)/packages-microsoft-prod.deb -O packages-microsoft-prod.deb @@ -48,10 +48,10 @@ sudo apt-get update sudo apt-get install procdump ``` -## Fedora 37 +## Fedora 38 #### 1. Register Microsoft key and feed ```sh -sudo rpm -Uvh https://packages.microsoft.com/config/fedora/37/packages-microsoft-prod.rpm +sudo rpm -Uvh https://packages.microsoft.com/config/fedora/38/packages-microsoft-prod.rpm ``` #### 2. Install Procdump @@ -59,10 +59,21 @@ sudo rpm -Uvh https://packages.microsoft.com/config/fedora/37/packages-microsoft sudo dnf install procdump ``` -## Fedora 38 +## Fedora 39 #### 1. Register Microsoft key and feed ```sh -sudo rpm -Uvh https://packages.microsoft.com/config/fedora/38/packages-microsoft-prod.rpm +sudo rpm -Uvh https://packages.microsoft.com/config/fedora/39/packages-microsoft-prod.rpm +``` + +#### 2. Install Procdump +```sh +sudo dnf install procdump +``` + +## Fedora 40 +#### 1. Register Microsoft key and feed +```sh +sudo rpm -Uvh https://packages.microsoft.com/config/fedora/40/packages-microsoft-prod.rpm ``` #### 2. Install Procdump diff --git a/README.md b/README.md index 3f36708..1251760 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Capture Usage: [-m|-ml Commit_Usage1[,Commit_Usage2...]] [-gcm [: | LOH: | POH:]Memory_Usage1[,Memory_Usage2...]] [-gcgen Generation] - [-restrack] + [-restrack [nodump]] [-sr Sample_Rate] [-tc Thread_Threshold] [-fc FileDescriptor_Threshold] @@ -39,7 +39,7 @@ Capture Usage: [-mc Custom_Dump_Mask] [-pf Polling_Frequency] [-o] - [-log] + [-log syslog|stdout] { {{[-w] Process_Name | [-pgid] PID} [Dump_File | Dump_Folder]} } @@ -53,7 +53,7 @@ Options: -ml Memory commit threshold(s) (MB) below which to create dumps. -gcm [.NET] GC memory threshold(s) (MB) above which to create dumps for the specified generation or heap (default is total .NET memory usage). -gcgen [.NET] Create dump when the garbage collection of the specified generation starts and finishes. - -restrack Enable memory leak tracking (malloc family of APIs). + -restrack Enable memory leak tracking (malloc family of APIs). Use the nodump option to prevent dump generation and only produce restrack report(s). -sr Sample rate when using -restrack. -tc Thread count threshold above which to create a dump of the process. -fc File descriptor count threshold above which to create a dump of the process. @@ -64,7 +64,7 @@ Options: -mc Custom core dump mask (in hex) indicating what memory should be included in the core dump. Please see 'man core' (/proc/[pid]/coredump_filter) for available options. -pf Polling frequency. -o Overwrite existing dump file. - -log Writes extended ProcDump tracing to syslog. + -log Writes extended ProcDump tracing to the specified output stream (syslog or stdout). -w Wait for the specified process to launch if it's not running. -pgid Process ID specified refers to a process group ID. ``` @@ -121,6 +121,10 @@ The following will create a core dump and a memory leak report when memory usage ``` sudo procdump -m 100 -restrack 1234 ``` +The following will create a memory leak report (no dumps) when memory usage is >= 100 MB +``` +sudo procdump -m 100 -restrack nodump 1234 +``` The following will create a core dump and a memory leak report when memory usage is >= 100 MB by sampling every 10th memory allocation. ``` sudo procdump -m 100 -restrack -sr 10 1234 diff --git a/dist/changelog b/dist/changelog index bfbff5a..2343308 100644 --- a/dist/changelog +++ b/dist/changelog @@ -1,3 +1,6 @@ +* Sat May 25 2024 Mario Hewardt - 3.3 +- Adds improvements related to containerized workflows + * Mon Feb 5 2024 Mario Hewardt - 3.2 - Adds mmap/munmap to resource tracking diff --git a/ebpf/procdump_ebpf.c b/ebpf/procdump_ebpf.c index c5f8dbb..423bd1c 100644 --- a/ebpf/procdump_ebpf.c +++ b/ebpf/procdump_ebpf.c @@ -18,11 +18,38 @@ #include "procdump_ebpf_common.h" pid_t target_PID; +uint dev, inode; int sampleRate; int currentSampleCount; +bool isLoggingEnabled; char LICENSE[] SEC("license") = "Dual BSD/GPL"; +// ------------------------------------------------------------------------------------------ +// GetFilterPidTgid +// +// Returns the PID and TID of the current process. +// ------------------------------------------------------------------------------------------ +__attribute__((always_inline)) +static inline bool GetFilterPidTgid(struct bpf_pidns_info* pidns) +{ + if(bpf_get_ns_current_pid_tgid(dev, inode, pidns, sizeof(*pidns))) + { + return false; + } + + // + // Only trace PIDs that matched the target PID and if we should sample this event. + // + if (pidns->tgid != target_PID) + { + return false; + } + + return true; +} + + // ------------------------------------------------------------------------------------------ // CheckSampleRate // @@ -66,17 +93,15 @@ static inline void ZeroMemory(char* ptr, unsigned int size) // Sends the allocation/free event // ------------------------------------------------------------------------------------------ __attribute__((always_inline)) -static inline int SendEvent(void* alloc) +static inline int SendEvent(void* alloc, bool freeOp, struct bpf_pidns_info* pidns) { struct ResourceInformation* event = NULL; - uint64_t pidTid = bpf_get_current_pid_tgid(); int ret = 1; // // Only trace PIDs that matched the target PID and have non NULL allocations // - uint64_t p = pidTid >> 32; - if (p != target_PID || alloc == NULL) + if (freeOp == false && alloc == NULL) { return 1; } @@ -84,34 +109,41 @@ static inline int SendEvent(void* alloc) // // Get event // - event = (struct ResourceInformation*) bpf_map_lookup_elem(&argsHashMap, &pidTid); + event = (struct ResourceInformation*) bpf_map_lookup_elem(&argsHashMap, &pidns->pid); if (event == NULL) { - BPF_PRINTK("[SendEvent] Failed: Getting event (allocation address: 0x%lx, target PID: %d)\n", alloc, target_PID); + BPF_PRINTK(" [SendEvent] Failed: Getting event (allocation address: 0x%lx, target PID: %d)", alloc, target_PID); return 1; } // - // Set the allocation address + // Set the allocation address if it's an allocation operation // - event->allocAddress = (unsigned long) alloc; + if(freeOp == false) + { + event->allocAddress = (unsigned long) alloc; + BPF_PRINTK(" [SendEvent] Allocation size:0x%lx", event->allocSize); + } + + {BPF_PRINTK(" [SendEvent] Allocation :0x%lx", event->allocAddress);} // // Send the event to the ring buffer (user land) // - if((ret = bpf_ringbuf_output(&ringBuffer, event, sizeof(*event), 0)) != 0) + if((ret = bpf_ringbuf_output(&ringBuffer, event, sizeof(*event), 0)) != 0) { - BPF_PRINTK("[SendEvent] Failed: Getting event (type: %d, allocation address: 0x%lx, target PID: %d)\n", event->resourceType, alloc, target_PID); + BPF_PRINTK(" [SendEvent] Failed: Getting event (type: %d, allocation address: 0x%lx, target PID: %d)", event->resourceType, event->allocAddress, target_PID); return ret; } - if((ret = bpf_map_delete_elem(&argsHashMap, &pidTid)) != 0) + {BPF_PRINTK(" [SendEvent] Deleting event for %ld", pidns->pid);} + if((ret = bpf_map_delete_elem(&argsHashMap, &pidns->pid)) != 0) { - BPF_PRINTK("[SendEvent] Failed: Getting event (type: %d, allocation address: 0x%lx, target PID: %d)\n", event->resourceType, alloc, target_PID); + BPF_PRINTK(" [SendEvent] Failed: Deleting event (type: %d, allocation address: 0x%lx, target PID: %d)", event->resourceType, event->allocAddress, target_PID); return ret; } - BPF_PRINTK("[SendEvent] Success: (type: %d, allocation address: 0x%lx, target PID: %d)\n", event->resourceType, alloc, target_PID); + BPF_PRINTK(" [SendEvent] Success: (type: %d, allocation address: 0x%lx, target PID: %d)", event->resourceType, event->allocAddress, target_PID); return 0; } @@ -121,28 +153,18 @@ static inline int SendEvent(void* alloc) // Helper for all the intercepted free functions. // ------------------------------------------------------------------------------------------ __attribute__((always_inline)) -static inline int ResourceFreeHelper(void* alloc) +static inline int ResourceFreeHelper(void* alloc, struct bpf_pidns_info* pidns) { struct ResourceInformation* event = NULL; - int element = 1; - uint64_t pidTid = bpf_get_current_pid_tgid(); - - // - // Only trace PIDs that matched the target PID - // - uint64_t pid = pidTid >> 32; - if (pid != target_PID || alloc == NULL) - { - return 0; - } + uint32_t map_id = bpf_get_smp_processor_id(); // // Get heap element from the map // - event = bpf_map_lookup_elem(&heapStorage, &element); - if (event == NULL) + event = bpf_map_lookup_elem(&heapStorage, &map_id); + if (event == NULL) { - BPF_PRINTK("[ResourceFreeHelper] Failed: Getting event (allocation: 0x%lx, target PID: %d)\n", alloc, target_PID); + BPF_PRINTK(" [ResourceFreeHelper] Failed: Getting event (allocation: 0x%lx, target PID: %d)", alloc, target_PID); return 1; } @@ -160,13 +182,13 @@ static inline int ResourceFreeHelper(void* alloc) // we free the allocation and update other fields (such as allocation ptr) and then // send to user mode. // - if (bpf_map_update_elem(&argsHashMap, &pidTid, event, BPF_ANY) != 0) + if (bpf_map_update_elem(&argsHashMap, &pidns->pid, event, BPF_ANY) != 0) { - BPF_PRINTK("[ResourceFreeHelper] Failed: Updating event (allocation: 0x%lx, target PID: %d)\n", alloc, target_PID); + BPF_PRINTK(" [ResourceFreeHelper] Failed: Updating event (allocation: 0x%lx, target PID: %d)", alloc, target_PID); return 1; } - BPF_PRINTK("[ResourceFreeHelper] Success: (allocation: 0x%lx, target PID: %d)\n", alloc, target_PID); + BPF_PRINTK(" [ResourceFreeHelper] Success: (allocation: 0x%lx, target PID: %d)", alloc, target_PID); return 0; } @@ -177,17 +199,15 @@ static inline int ResourceFreeHelper(void* alloc) // Helper for all the intercepted allocation functions. // ------------------------------------------------------------------------------------------ __attribute__((always_inline)) -static inline int ResourceAllocHelper(unsigned long size, struct pt_regs *ctx) +static inline int ResourceAllocHelper(unsigned long size, struct pt_regs *ctx, struct bpf_pidns_info* pidns) { struct ResourceInformation* event = NULL; - uint64_t pidTid = bpf_get_current_pid_tgid(); - int element = 0; + uint32_t map_id = bpf_get_smp_processor_id(); // - // Only trace PIDs that matched the target PID and if we should sample this event. + // Only trace if we should sample this event. // - uint64_t pid = pidTid >> 32; - if (pid != target_PID || CheckSampleRate() == false) + if (CheckSampleRate() == false) { return 0; } @@ -195,14 +215,14 @@ static inline int ResourceAllocHelper(unsigned long size, struct pt_regs *ctx) // // Get heap element from the map // - event = bpf_map_lookup_elem(&heapStorage, &element); - if (event != NULL) + event = bpf_map_lookup_elem(&heapStorage, &map_id); + if (event != NULL) { - ZeroMemory((char*) event, sizeof(struct ResourceInformation)); + ZeroMemory((char*) event, sizeof(struct ResourceInformation)); } else { - BPF_PRINTK("[ResourceAllocHelper] Failed: Getting event (allocation size: 0x%lx, target PID: %d)\n", size, target_PID); + BPF_PRINTK(" [ResourceAllocHelper] Failed: Getting event (allocation size: 0x%lx, target PID: %d)", size, target_PID); return 1; } @@ -220,13 +240,13 @@ static inline int ResourceAllocHelper(unsigned long size, struct pt_regs *ctx) // we exit the malloc and update other fields (such as allocation ptr) and then // send to user mode. // - if (bpf_map_update_elem(&argsHashMap, &pidTid, event, BPF_ANY) != 0) + if (bpf_map_update_elem(&argsHashMap, &pidns->pid, event, BPF_ANY) != 0) { - BPF_PRINTK("[ResourceAllocHelper] Failed: Updating event (allocation size: 0x%lx, target PID: %d)\n", size, target_PID); + BPF_PRINTK(" [ResourceAllocHelper] Failed: Updating event (allocation size: 0x%lx, target PID: %d)", size, target_PID); return 1; } - BPF_PRINTK("[ResourceAllocHelper] Success: (allocation size: 0x%lx, target PID: %d)\n", size, target_PID); + BPF_PRINTK(" [ResourceAllocHelper] Success: (allocation size: 0x%lx, target PID: %d)", size, target_PID); return 0; } @@ -236,7 +256,14 @@ static inline int ResourceAllocHelper(unsigned long size, struct pt_regs *ctx) SEC("uprobe/libc.so.6:mmap") int sys_mmap_enter(struct pt_regs *ctx) { - ResourceAllocHelper((unsigned long) PT_REGS_PARM2(ctx), ctx); + struct bpf_pidns_info pidns = {}; + if(GetFilterPidTgid(&pidns) == false) + { + return 0; + } + + {BPF_PRINTK("[***** sys_mmap_enter, pid: %ld, tgid: %ld, size: %ld]", pidns.pid, pidns.tgid, (unsigned long) PT_REGS_PARM2(ctx));} + ResourceAllocHelper((unsigned long) PT_REGS_PARM2(ctx), ctx, &pidns); return 0; } @@ -246,7 +273,14 @@ int sys_mmap_enter(struct pt_regs *ctx) SEC("uretprobe/libc.so.6:mmap") int sys_mmap_exit(struct pt_regs *ctx) { - SendEvent((void*) PT_REGS_RC(ctx)); + struct bpf_pidns_info pidns = {}; + if(GetFilterPidTgid(&pidns) == false) + { + return 0; + } + + {BPF_PRINTK("[***** sys_mmap_exit, pid: %ld, tgid: %ld]", pidns.pid, pidns.tgid);} + SendEvent((void*) PT_REGS_RC(ctx), false, &pidns); return 0; } @@ -256,7 +290,14 @@ int sys_mmap_exit(struct pt_regs *ctx) SEC("uprobe/libc.so.6:munmap") int sys_munmap_enter(struct pt_regs *ctx) { - ResourceFreeHelper((void*) PT_REGS_PARM1(ctx)); + struct bpf_pidns_info pidns = {}; + if(GetFilterPidTgid(&pidns) == false) + { + return 0; + } + + {BPF_PRINTK("[***** sys_munmap_enter, pid: %ld, tgid: %ld]", pidns.pid, pidns.tgid);} + ResourceFreeHelper((void*) PT_REGS_PARM1(ctx), &pidns); return 0; } @@ -266,7 +307,14 @@ int sys_munmap_enter(struct pt_regs *ctx) SEC("uretprobe/libc.so.6:munmap") int sys_munmap_exit(struct pt_regs *ctx) { - SendEvent((void*) PT_REGS_PARM1(ctx)); + struct bpf_pidns_info pidns = {}; + if(GetFilterPidTgid(&pidns) == false) + { + return 0; + } + + {BPF_PRINTK("[***** sys_munmap_exit, pid: %ld, tgid: %ld]", pidns.pid, pidns.tgid);} + SendEvent(NULL, true, &pidns); return 0; } @@ -276,7 +324,14 @@ int sys_munmap_exit(struct pt_regs *ctx) SEC("uprobe/libc.so.6:malloc") int BPF_KPROBE(uprobe_malloc, unsigned long size) { - ResourceAllocHelper(size, ctx); + struct bpf_pidns_info pidns = {}; + if(GetFilterPidTgid(&pidns) == false) + { + return 0; + } + + {BPF_PRINTK("[***** malloc_enter, pid:%ld, tgid: %ld, size: %ld]", pidns.pid, pidns.tgid, size);} + ResourceAllocHelper(size, ctx, &pidns); return 0; } @@ -286,7 +341,14 @@ int BPF_KPROBE(uprobe_malloc, unsigned long size) SEC("uretprobe/libc.so.6:malloc") int BPF_KRETPROBE(uretprobe_malloc, void* ret) { - SendEvent(ret); + struct bpf_pidns_info pidns = {}; + if(GetFilterPidTgid(&pidns) == false) + { + return 0; + } + + {BPF_PRINTK("[***** malloc_exit, pid: %ld, tgid: %ld]", pidns.pid, pidns.tgid);} + SendEvent(ret, false, &pidns); return 0; } @@ -294,9 +356,16 @@ int BPF_KRETPROBE(uretprobe_malloc, void* ret) // uprobe_free // ------------------------------------------------------------------------------------------ SEC("uprobe/libc.so.6:free") -int BPF_KRETPROBE(uprobe_free, void* alloc) +int BPF_KPROBE(uprobe_free, void* alloc) { - ResourceFreeHelper(alloc); + struct bpf_pidns_info pidns = {}; + if(GetFilterPidTgid(&pidns) == false) + { + return 0; + } + + {BPF_PRINTK("[***** free_enter, pid: %ld, tgid: %ld]", pidns.pid, pidns.tgid);} + ResourceFreeHelper(alloc, &pidns); return 0; } @@ -307,7 +376,14 @@ int BPF_KRETPROBE(uprobe_free, void* alloc) SEC("uretprobe/libc.so.6:free") int BPF_KRETPROBE(uretprobe_free, void* ret) { - SendEvent(ret); + struct bpf_pidns_info pidns = {}; + if(GetFilterPidTgid(&pidns) == false) + { + return 0; + } + + {BPF_PRINTK("[***** free_exit, pid: %ld, tgid: %ld]", pidns.pid, pidns.tgid);} + SendEvent(NULL, true, &pidns); return 0; } @@ -319,7 +395,14 @@ int BPF_KRETPROBE(uretprobe_free, void* ret) SEC("uprobe/libc.so.6:calloc") int BPF_KPROBE(uprobe_calloc, int count, unsigned long size) { - ResourceAllocHelper(size, ctx); + struct bpf_pidns_info pidns = {}; + if(GetFilterPidTgid(&pidns) == false) + { + return 0; + } + + {BPF_PRINTK("[***** calloc_enter, pid: %ld, tgid: %ld, size: %ld]", pidns.pid, pidns.tgid, size*count);} + ResourceAllocHelper(size*count, ctx, &pidns); return 0; } @@ -331,7 +414,14 @@ int BPF_KPROBE(uprobe_calloc, int count, unsigned long size) SEC("uretprobe/libc.so.6:calloc") int BPF_KRETPROBE(uretprobe_calloc, void* ret) { - SendEvent(ret); + struct bpf_pidns_info pidns = {}; + if(GetFilterPidTgid(&pidns) == false) + { + return 0; + } + + {BPF_PRINTK("[***** calloc_exit, pid: %ld, tgid: %ld]", pidns.pid, pidns.tgid);} + SendEvent(ret, false, &pidns); return 0; } @@ -343,7 +433,14 @@ int BPF_KRETPROBE(uretprobe_calloc, void* ret) SEC("uprobe/libc.so.6:realloc") int BPF_KPROBE(uprobe_realloc, void* ptr, unsigned long size) { - ResourceAllocHelper(size, ctx); + struct bpf_pidns_info pidns = {}; + if(GetFilterPidTgid(&pidns) == false) + { + return 0; + } + + {BPF_PRINTK("[***** realloc_enter, pid:%ld, tgid: %ld, size:%ld]", pidns.pid, pidns.tgid, size);} + ResourceAllocHelper(size, ctx, &pidns); return 0; } @@ -355,7 +452,14 @@ int BPF_KPROBE(uprobe_realloc, void* ptr, unsigned long size) SEC("uretprobe/libc.so.6:realloc") int BPF_KRETPROBE(uretprobe_realloc, void* ret) { - SendEvent(ret); + struct bpf_pidns_info pidns = {}; + if(GetFilterPidTgid(&pidns) == false) + { + return 0; + } + + {BPF_PRINTK("[***** realloc_exit, pid: %ld, tgid: %ld]", pidns.pid, pidns.tgid);} + SendEvent(ret, false, &pidns); return 0; } @@ -368,8 +472,15 @@ int BPF_KRETPROBE(uretprobe_realloc, void* ret) SEC("uprobe/libc.so.6:reallocarray") int BPF_KPROBE(uprobe_reallocarray, void* ptr, long count, unsigned long size) { - ResourceAllocHelper(size, ctx); - return 0; + struct bpf_pidns_info pidns = {}; + if(GetFilterPidTgid(&pidns) == false) + { + return 0; + } + + {BPF_PRINTK("[***** reallocarray_enter, pid: %ld, tgid: %ld, size: %ld]", pidns.pid, pidns.tgid, size*count);} + ResourceAllocHelper(size*count, ctx, &pidns); + return 0; } // ------------------------------------------------------------------------------------------ @@ -380,6 +491,13 @@ int BPF_KPROBE(uprobe_reallocarray, void* ptr, long count, unsigned long size) SEC("uretprobe/libc.so.6:reallocarray") int BPF_KRETPROBE(uretprobe_reallocarray, void* ret) { - SendEvent(ret); + struct bpf_pidns_info pidns = {}; + if(GetFilterPidTgid(&pidns) == false) + { + return 0; + } + + {BPF_PRINTK("[***** reallocarray_exit, pid: %ld, tgid: %ld]", pidns.pid, pidns.tgid);} + SendEvent(ret, false, &pidns); return 0; } \ No newline at end of file diff --git a/ebpf/procdump_ebpf.h b/ebpf/procdump_ebpf.h index 26ae33a..94a2f2c 100644 --- a/ebpf/procdump_ebpf.h +++ b/ebpf/procdump_ebpf.h @@ -24,13 +24,12 @@ #define USER_STACKID_FLAGS (0 | BPF_F_FAST_STACK_CMP | BPF_F_USER_STACK) #define ARGS_HASH_SIZE 10240 -#ifdef DEBUG_K #define BPF_PRINTK( format, ... ) \ - char fmt[] = format; \ - bpf_trace_printk(fmt, sizeof(fmt), ##__VA_ARGS__ ); -#else -#define BPF_PRINTK ((void)0); -#endif + if(isLoggingEnabled == true) \ + { \ + char fmt[] = format; \ + bpf_trace_printk(fmt, sizeof(fmt), ##__VA_ARGS__ ); \ + } // // This is a hashmap to hold resource arguments (such as size) between alloc and free calls. @@ -40,10 +39,11 @@ struct argsStruct unsigned long size; }; -struct { +struct +{ __uint(type, BPF_MAP_TYPE_HASH); __uint(max_entries, ARGS_HASH_SIZE); - __type(key, uint64_t); + __type(key, int); __type(value, struct ResourceInformation); } argsHashMap SEC(".maps"); @@ -51,9 +51,10 @@ struct { // // Since stack space is minimal, we use this to store an event on the heap // -struct { +struct +{ __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); - __uint(max_entries, 2); + __uint(max_entries, 512); __type(key, int); __type(value, struct ResourceInformation); } heapStorage SEC(".maps"); @@ -61,7 +62,8 @@ struct { // // The ring buffer we use to communicate with user space // -struct { +struct +{ __uint(type, BPF_MAP_TYPE_RINGBUF); __uint(max_entries, 10 * 1024 * 1024 /* 10 MB */); } ringBuffer SEC(".maps"); diff --git a/include/CoreDumpWriter.h b/include/CoreDumpWriter.h index d8dd0b8..ebfdc7f 100644 --- a/include/CoreDumpWriter.h +++ b/include/CoreDumpWriter.h @@ -81,6 +81,7 @@ struct CoreDumpWriter { struct CoreDumpWriter *NewCoreDumpWriter(enum ECoreDumpType type, struct ProcDumpConfiguration *config); char* WriteCoreDumpInternal(struct CoreDumpWriter *self, char* socketName); char* WriteCoreDump(struct CoreDumpWriter *self); -char* GetCoreDumpName(pid_t pid, char* procName, char* dumpPath, char* dumpName, enum ECoreDumpType type); +char* GetCoreDumpPrefixName(pid_t pid, char* procName, char* dumpPath, char* dumpName, enum ECoreDumpType type); +char* GetCoreDumpName(ProcDumpConfiguration* config, ECoreDumpType type); #endif // CORE_DUMP_WRITER_H \ No newline at end of file diff --git a/include/Logging.h b/include/Logging.h index 04a4ad2..cd5108a 100644 --- a/include/Logging.h +++ b/include/Logging.h @@ -19,7 +19,7 @@ #include #include -#define INTERNAL_ERROR "Internal Error has occurred. If problem continues to occur run procdump with -log flag to trace issue (traces go into syslog)" +#define INTERNAL_ERROR "Internal Error has occurred. If problem continues to occur run procdump with -log flag to trace issue." // double-macro-stringify to expand __FILE__ and __LINE__ properly when they are injected in files #define S1(x) #x diff --git a/include/ProcDumpConfiguration.h b/include/ProcDumpConfiguration.h index 65156ac..916ad24 100644 --- a/include/ProcDumpConfiguration.h +++ b/include/ProcDumpConfiguration.h @@ -60,6 +60,13 @@ struct MonitoredProcessMapEntry long long starttime; }; +enum DiagnosticsLogTarget +{ + none, + diag_syslog, + diag_stdout +}; + struct ProcDumpConfiguration { // Process and System info @@ -73,6 +80,7 @@ struct ProcDumpConfiguration // Runtime Values int NumberOfDumpsCollecting; // Number of dumps we're collecting int NumberOfDumpsCollected; // Number of dumps we have collected + int NumberOfLeakReportsCollected; // Number of leak reports we have collected bool bTerminated; // Do we know whether the process has terminated and subsequently whether we are terminating? char* socketPath; bool bExitProcessMonitor; @@ -101,7 +109,7 @@ struct ProcDumpConfiguration bool bTimerThreshold; // -s int NumberOfDumpsToCollect; // -n bool WaitingForProcessName; // -w - bool DiagnosticsLoggingEnabled; // -log + DiagnosticsLogTarget DiagnosticsLoggingEnabled; // -log int ThreadThreshold; // -tc int FileDescriptorThreshold; // -fc int* SignalNumber; // -sig @@ -114,6 +122,7 @@ struct ProcDumpConfiguration char *ExceptionFilter; // -f (unfortunately we named this ExceptionFilter event hough it can be used for other include filters as well) char *ExcludeFilter; // -fx (exclude filter) bool bRestrackEnabled; // -restrack + bool bRestrackGenerateDump; // -restrack generate dump flag bool bLeakReportInProgress; int SampleRate; // Record every X resource allocation in restrack int CoreDumpMask; // -mc (core dump mask) diff --git a/include/ProcDumpVersion.h.in b/include/ProcDumpVersion.h.in index fa8a697..01a7119 100644 --- a/include/ProcDumpVersion.h.in +++ b/include/ProcDumpVersion.h.in @@ -32,5 +32,5 @@ // #define VER_COMPANY "Sysinternals - www.sysinternals.com" -#define VER_COPYRIGHT "Copyright (C) 2009-2023 Microsoft Corporation. All rights reserved. Licensed under the MIT license.\nMark Russinovich, Mario Hewardt, John Salem, Javid Habibi." +#define VER_COPYRIGHT "Copyright (C) 2009-2024 Microsoft Corporation. All rights reserved. Licensed under the MIT license.\nMark Russinovich, Mario Hewardt, John Salem, Javid Habibi." diff --git a/include/Process.h b/include/Process.h index 654962a..189798f 100644 --- a/include/Process.h +++ b/include/Process.h @@ -201,6 +201,12 @@ struct ProcessStat { // NOTE: This does not come from /proc/[pid]/stat rather is populated by enumerating the /proc/>/fdinfo int num_filedescriptors; + + // NOTE: Populated by enumerating the /proc/>/status + uid_t real_uid; + uid_t effective_uid; + uid_t saved_uid; + uid_t fs_uid; }; // @@ -272,7 +278,7 @@ struct ProcessStatus { }; // ----------------------------------------------------------- -// a series of functions for collecting infromation from /procfs +// a series of functions for collecting information from /procfs // ----------------------------------------------------------- bool GetProcessStat(pid_t pid, struct ProcessStat *proc); diff --git a/include/Restrack.h b/include/Restrack.h index 40311ee..9ca22d3 100644 --- a/include/Restrack.h +++ b/include/Restrack.h @@ -16,7 +16,7 @@ struct procdump_ebpf* RunRestrack(struct ProcDumpConfiguration *config); void StopRestrack(struct procdump_ebpf* skel); int RestrackHandleEvent(void *ctx, void *data, size_t data_sz); void* ReportLeaks(void* args); -pthread_t WriteRestrackSnapshot(ProcDumpConfiguration* config, const char* filename); +pthread_t WriteRestrackSnapshot(ProcDumpConfiguration* config, ECoreDumpType type); #endif // RESTRACK_H diff --git a/procdump.1 b/procdump.1 index 654cd1e..b9e58dc 100644 --- a/procdump.1 +++ b/procdump.1 @@ -1,5 +1,5 @@ .\" Manpage for procdump. -.TH man 8 "2/5/2024" "3.2" "procdump manpage" +.TH man 8 "2/5/2024" "3.3" "procdump manpage" .SH NAME procdump \- generate coredumps based off performance triggers. .SH SYNOPSIS @@ -9,7 +9,7 @@ procdump [-n Count] [-m|-ml Commit_Usage1[,Commit_Usage2...]] [-gcm [: | LOH: | POH:]Memory_Usage1[,Memory_Usage2...]] [-gcgen Generation] - [-restrack] + [-restrack [nodump]] [-sr Sample_Rate] [-tc Thread_Threshold] [-fc FileDescriptor_Threshold] @@ -20,7 +20,7 @@ procdump [-n Count] [-mc Custom_Dump_Mask] [-pf Polling_Frequency] [-o] - [-log] + [-log syslog|stdout] { {{[-w] Process_Name | [-pgid] PID} [Dump_File | Dump_Folder]} } @@ -34,7 +34,7 @@ Options: -ml Memory commit threshold(s) (MB) below which to create dumps. -gcm [.NET] GC memory threshold(s) (MB) above which to create dumps for the specified generation or heap (default is total .NET memory usage). -gcgen [.NET] Create dump when the garbage collection of the specified generation starts and finishes. - -restrack Enable memory leak tracking (malloc family of APIs). + -restrack Enable memory leak tracking (malloc family of APIs). Use the nodump option to prevent dump generation and only produce restrack report(s). -sr Sample rate when using -restrack. -tc Thread count threshold above which to create a dump of the process. -fc File descriptor count threshold above which to create a dump of the process. @@ -45,7 +45,7 @@ Options: -mc Custom core dump mask (in hex) indicating what memory should be included in the core dump. Please see 'man core' (/proc/[pid]/coredump_filter) for available options. -pf Polling frequency. -o Overwrite existing dump file. - -log Writes extended ProcDump tracing to syslog. + -log Writes extended ProcDump tracing to the specified output stream (syslog or stdout). -w Wait for the specified process to launch if it's not running. -pgid Process ID specified refers to a process group ID. diff --git a/src/CoreDumpWriter.cpp b/src/CoreDumpWriter.cpp index 129f785..3291263 100644 --- a/src/CoreDumpWriter.cpp +++ b/src/CoreDumpWriter.cpp @@ -8,6 +8,8 @@ //-------------------------------------------------------------------- #include "Includes.h" +#include + static const char *CoreDumpTypeStrings[] = { "commit", "cpu", "thread", "filedesc", "signal", "time", "exception", "manual" }; //-------------------------------------------------------------------- @@ -32,13 +34,47 @@ struct CoreDumpWriter *NewCoreDumpWriter(enum ECoreDumpType type, struct ProcDum return writer; } - //-------------------------------------------------------------------- // // GetCoreDumpName - Gets the core dump name // //-------------------------------------------------------------------- -char* GetCoreDumpName(pid_t pid, char* procName, char* dumpPath, char* dumpName, enum ECoreDumpType type) +char* GetCoreDumpName(ProcDumpConfiguration* config, ECoreDumpType type) +{ + char* name = sanitize(config->ProcessName); + char* gcorePrefixName = GetCoreDumpPrefixName(config->ProcessId, name, config->CoreDumpPath, config->CoreDumpName, type); + char* dumpName = (char*) malloc(PATH_MAX+1); + if(!dumpName) + { + Log(error, INTERNAL_ERROR); + Trace("GetCoreDumpName: Memory allocation failure"); + free(name); + free(gcorePrefixName); + return NULL; + } + + if(snprintf(dumpName, PATH_MAX, "%s.%d", gcorePrefixName, config->ProcessId) < 0) + { + Log(error, INTERNAL_ERROR); + Trace("GetCoreDumpName: failed sprintf core file name"); + free(dumpName); + free(name); + free(gcorePrefixName); + return NULL; + } + + free(name); + free(gcorePrefixName); + + return dumpName; +} + +//-------------------------------------------------------------------- +// +// GetCoreDumpPrefixName - Gets the core dump prefix name +// +//-------------------------------------------------------------------- +char* GetCoreDumpPrefixName(pid_t pid, char* procName, char* dumpPath, char* dumpName, enum ECoreDumpType type) { auto_free char *name = sanitize(procName); time_t rawTime = {0}; @@ -50,7 +86,7 @@ char* GetCoreDumpName(pid_t pid, char* procName, char* dumpPath, char* dumpName, if(!gcorePrefixName) { Log(error, INTERNAL_ERROR); - Trace("GetCoreDumpName: Memory allocation failure"); + Trace("GetCoreDumpPrefixName: Memory allocation failure"); exit(-1); } @@ -196,7 +232,7 @@ char* WriteCoreDumpInternal(struct CoreDumpWriter *self, char* socketName) char *name = sanitize(self->Config->ProcessName); pid_t pid = self->Config->ProcessId; - gcorePrefixName = GetCoreDumpName(self->Config->ProcessId, name, self->Config->CoreDumpPath, self->Config->CoreDumpName, self->Type); + gcorePrefixName = GetCoreDumpPrefixName(self->Config->ProcessId, name, self->Config->CoreDumpPath, self->Config->CoreDumpName, self->Type); // assemble the command if(snprintf(command, BUFFER_LENGTH, "gcore -o %s %d 2>&1", gcorePrefixName, pid) < 0) diff --git a/src/Logging.cpp b/src/Logging.cpp index c171e97..ca7918f 100644 --- a/src/Logging.cpp +++ b/src/Logging.cpp @@ -12,7 +12,7 @@ static const char *LogLevelStrings[] = { "DEBUG", "INFO", "WARN", "CRITICAL", "E extern struct ProcDumpConfiguration g_config; pthread_mutex_t LoggerLock; -void LogFormatter(enum LogLevel logLevel, const char *message, va_list args) +void LogFormatter(enum LogLevel logLevel, enum DiagnosticsLogTarget target, const char *message, va_list args) { char timeBuff[64]; time_t rawTime; @@ -46,9 +46,17 @@ void LogFormatter(enum LogLevel logLevel, const char *message, va_list args) { puts(trace); } - - // All log entries also go to the syslog - syslog(LOG_DEBUG, "%s", trace); + else + { + if(target == diag_syslog) + { + syslog(LOG_DEBUG, "%s", trace); + } + else if(target == diag_stdout) + { + puts(trace); + } + } va_end(copy); free(trace); @@ -59,7 +67,7 @@ void Log(enum LogLevel logLevel, const char *message, ...) { va_list args; va_start(args, message); - LogFormatter(logLevel, message, args); + LogFormatter(logLevel, diag_stdout, message, args); va_end(args); } @@ -68,6 +76,11 @@ void DiagTrace(const char *message, ...) { va_list args; va_start(args, message); - if(g_config.DiagnosticsLoggingEnabled) LogFormatter(debug, message, args); + + if(g_config.DiagnosticsLoggingEnabled != none) + { + LogFormatter(debug, g_config.DiagnosticsLoggingEnabled, message, args); + } + va_end(args); } diff --git a/src/Monitor.cpp b/src/Monitor.cpp index 1af8e5f..1b3f07e 100644 --- a/src/Monitor.cpp +++ b/src/Monitor.cpp @@ -13,6 +13,7 @@ #include #include +#include static pthread_t sig_thread_id; @@ -168,6 +169,29 @@ ProcDumpConfiguration* GetNewMonitorConfiguration(ProcDumpConfiguration* sourceC return config; } +//-------------------------------------------------------------------- +// +// CheckAccess +// +// Checks to make sure we have access to the target process. +// +//-------------------------------------------------------------------- +bool CheckAccess(struct ProcDumpConfiguration *self) +{ + struct ProcessStat proc; + if(GetProcessStat(self->ProcessId, &proc) == false) + { + return false; + } + + uid_t euid = geteuid(); + if(euid == 0 || euid == proc.effective_uid) + { + return true; + } + + return false; +} //-------------------------------------------------------------------- // @@ -380,7 +404,10 @@ void MonitorProcesses(struct ProcDumpConfiguration *self) pthread_mutex_lock(&activeConfigurationsMutex); for (auto it = activeConfigurations.begin(); it != activeConfigurations.end(); ) { - if(it->second->bTerminated || it->second->nQuit || it->second->NumberOfDumpsCollected == it->second->NumberOfDumpsToCollect) + if (it->second->bTerminated || + it->second->nQuit || + it->second->NumberOfDumpsCollected == it->second->NumberOfDumpsToCollect || + it->second->NumberOfLeakReportsCollected == it->second->NumberOfDumpsToCollect) { Log(info, "Stopping monitors for process: %s (%d)", it->second->ProcessName, it->second->ProcessId); WaitForAllMonitorsToTerminate(it->second); @@ -416,7 +443,10 @@ void MonitorProcesses(struct ProcDumpConfiguration *self) for (auto it = activeConfigurations.begin(); it != activeConfigurations.end(); ) { - if(it->second->bTerminated || it->second->nQuit || it->second->NumberOfDumpsCollected == it->second->NumberOfDumpsToCollect) + if (it->second->bTerminated || + it->second->nQuit || + it->second->NumberOfDumpsCollected == it->second->NumberOfDumpsToCollect || + it->second->NumberOfLeakReportsCollected == it->second->NumberOfDumpsToCollect) { SetQuit(it->second, 1); WaitForAllMonitorsToTerminate(it->second); @@ -579,6 +609,12 @@ int StartMonitor(struct ProcDumpConfiguration* monitorConfig) { int ret = 0; + if(CheckAccess(monitorConfig) == false) + { + Log(error, "Procdump is not running with elevated credentials or the effective uid does not match the effective uid of the target process (pid %d).", monitorConfig->ProcessId); + return -1; + } + if(CreateMonitorThreads(monitorConfig) != 0) { Log(error, INTERNAL_ERROR); @@ -682,7 +718,8 @@ pthread_t GetRestrackThread(struct ProcDumpConfiguration *self) //-------------------------------------------------------------------- int CancelRestrackThread(struct ProcDumpConfiguration *self) { - Trace("CancelRestrackThread: Enter"); + Trace("CancelRestrackThread: Enter [id=%d]", gettid()); + int rc = 0; pthread_t restrackThread = 0; @@ -691,10 +728,10 @@ int CancelRestrackThread(struct ProcDumpConfiguration *self) if(restrackThread != 0) { Trace("CancelRestrackThread: cancel restrack thread"); - rc = pthread_cancel(restrackThread); + SetQuit(self, 1); } - Trace("CancelRestrackThread: Exit"); + Trace("CancelRestrackThread: Exit [id=%d]", gettid()); return rc; } @@ -807,7 +844,7 @@ bool ContinueMonitoring(struct ProcDumpConfiguration *self) } // Have we reached the dump limit? - if (self->NumberOfDumpsCollected >= self->NumberOfDumpsToCollect) + if (self->NumberOfDumpsCollected >= self->NumberOfDumpsToCollect || self->NumberOfLeakReportsCollected >= self->NumberOfDumpsToCollect) { return false; } @@ -910,10 +947,15 @@ void *CommitMonitoringThread(void *thread_args /* struct ProcDumpConfiguration* (!config->bMemoryTriggerBelowValue && (memUsage >= config->MemoryThreshold[config->MemoryCurrentThreshold]))) { Log(info, "Trigger: Commit usage:%ldMB on process ID: %d", memUsage, config->ProcessId); - dumpFileName = WriteCoreDump(writer); - if(dumpFileName == NULL) + + if(config->bRestrackGenerateDump == true) { - SetQuit(config, 1); + // Only generate core dump if user did not specify the "nodump" restrack option + dumpFileName = WriteCoreDump(writer); + if(dumpFileName == NULL) + { + SetQuit(config, 1); + } } // @@ -921,7 +963,7 @@ void *CommitMonitoringThread(void *thread_args /* struct ProcDumpConfiguration* // if(config->bRestrackEnabled == true) { - pthread_t id = WriteRestrackSnapshot(config, (std::string(dumpFileName) + ".restrack").c_str()); + pthread_t id = WriteRestrackSnapshot(config, writer->Type); if (id == 0) { SetQuit(config, 1); @@ -985,10 +1027,15 @@ void* ThreadCountMonitoringThread(void *thread_args /* struct ProcDumpConfigurat if (proc.num_threads >= config->ThreadThreshold) { Log(info, "Trigger: Thread count:%ld on process ID: %d", proc.num_threads, config->ProcessId); - dumpFileName = WriteCoreDump(writer); - if(dumpFileName == NULL) + + if(config->bRestrackGenerateDump == true) { - SetQuit(config, 1); + // Only generate core dump if user did not specify the "nodump" restrack option + dumpFileName = WriteCoreDump(writer); + if(dumpFileName == NULL) + { + SetQuit(config, 1); + } } // @@ -996,7 +1043,7 @@ void* ThreadCountMonitoringThread(void *thread_args /* struct ProcDumpConfigurat // if(config->bRestrackEnabled == true) { - pthread_t id = WriteRestrackSnapshot(config, (std::string(dumpFileName) + ".restrack").c_str()); + pthread_t id = WriteRestrackSnapshot(config, writer->Type); if (id == 0) { SetQuit(config, 1); @@ -1059,10 +1106,14 @@ void* FileDescriptorCountMonitoringThread(void *thread_args /* struct ProcDumpCo if (proc.num_filedescriptors >= config->FileDescriptorThreshold) { Log(info, "Trigger: File descriptors:%ld on process ID: %d", proc.num_filedescriptors, config->ProcessId); - dumpFileName = WriteCoreDump(writer); - if(dumpFileName == NULL) + if(config->bRestrackGenerateDump == true) { - SetQuit(config, 1); + // Only generate core dump if user did not specify the "nodump" restrack option + dumpFileName = WriteCoreDump(writer); + if(dumpFileName == NULL) + { + SetQuit(config, 1); + } } // @@ -1070,7 +1121,7 @@ void* FileDescriptorCountMonitoringThread(void *thread_args /* struct ProcDumpCo // if(config->bRestrackEnabled == true) { - pthread_t id = WriteRestrackSnapshot(config, (std::string(dumpFileName) + ".restrack").c_str()); + pthread_t id = WriteRestrackSnapshot(config, writer->Type); if (id == 0) { SetQuit(config, 1); @@ -1174,12 +1225,17 @@ void* SignalMonitoringThread(void *thread_args /* struct ProcDumpConfiguration* // Write core dump Log(info, "Trigger: Signal:%d on process ID: %d", signum, config->ProcessId); - dumpFileName = WriteCoreDump(writer); - if(dumpFileName == NULL) + + if(config->bRestrackGenerateDump == true) { - ptrace(PTRACE_CONT, config->ProcessId, NULL, signum); - ptrace(PTRACE_DETACH, config->ProcessId, 0, 0); - break; + // Only generate core dump if user did not specify the "nodump" restrack option + dumpFileName = WriteCoreDump(writer); + if(dumpFileName == NULL) + { + ptrace(PTRACE_CONT, config->ProcessId, NULL, signum); + ptrace(PTRACE_DETACH, config->ProcessId, 0, 0); + break; + } } // @@ -1187,7 +1243,7 @@ void* SignalMonitoringThread(void *thread_args /* struct ProcDumpConfiguration* // if(config->bRestrackEnabled == true) { - pthread_t id = WriteRestrackSnapshot(config, (std::string(dumpFileName) + ".restrack").c_str()); + pthread_t id = WriteRestrackSnapshot(config, writer->Type); if (id != 0) { leakReportThreads.push_back(id); @@ -1196,7 +1252,7 @@ void* SignalMonitoringThread(void *thread_args /* struct ProcDumpConfiguration* ptrace(PTRACE_CONT, config->ProcessId, NULL, signum); - if(config->NumberOfDumpsCollected >= config->NumberOfDumpsToCollect) + if(config->NumberOfDumpsCollected >= config->NumberOfDumpsToCollect || config->NumberOfLeakReportsCollected >= config->NumberOfDumpsToCollect) { // If we are over the max number of dumps to collect, send the original signal we intercepted. pthread_mutex_unlock(&config->ptrace_mutex); @@ -1273,10 +1329,14 @@ void *CpuMonitoringThread(void *thread_args /* struct ProcDumpConfiguration* */) (!config->bCpuTriggerBelowValue && (cpuUsage >= config->CpuThreshold))) { Log(info, "Trigger: CPU usage:%d%% on process ID: %d", cpuUsage, config->ProcessId); - dumpFileName = WriteCoreDump(writer); - if(dumpFileName == NULL) + if(config->bRestrackGenerateDump == true) { - SetQuit(config, 1); + // Only generate core dump if user did not specify the "nodump" restrack option + dumpFileName = WriteCoreDump(writer); + if(dumpFileName == NULL) + { + SetQuit(config, 1); + } } // @@ -1284,7 +1344,7 @@ void *CpuMonitoringThread(void *thread_args /* struct ProcDumpConfiguration* */) // if(config->bRestrackEnabled == true) { - pthread_t id = WriteRestrackSnapshot(config, (std::string(dumpFileName) + ".restrack").c_str()); + pthread_t id = WriteRestrackSnapshot(config, writer->Type); if (id == 0) { SetQuit(config, 1); @@ -1342,10 +1402,14 @@ void *TimerThread(void *thread_args /* struct ProcDumpConfiguration* */) while ((rc = WaitForQuit(config, 0)) == WAIT_TIMEOUT) { Log(info, "Trigger: Timer:%ld(s) on process ID: %d", config->PollingInterval/1000, config->ProcessId); - dumpFileName = WriteCoreDump(writer); - if(dumpFileName == NULL) + if(config->bRestrackGenerateDump == true) { - SetQuit(config, 1); + // Only generate core dump if user did not specify the "nodump" restrack option + dumpFileName = WriteCoreDump(writer); + if(dumpFileName == NULL) + { + SetQuit(config, 1); + } } // @@ -1353,7 +1417,7 @@ void *TimerThread(void *thread_args /* struct ProcDumpConfiguration* */) // if(config->bRestrackEnabled == true) { - pthread_t id = WriteRestrackSnapshot(config, (std::string(dumpFileName) + ".restrack").c_str()); + pthread_t id = WriteRestrackSnapshot(config, writer->Type); if (id == 0) { SetQuit(config, 1); @@ -1924,9 +1988,24 @@ void *WaitForProfilerCompletion(void *thread_args /* struct ProcDumpConfiguratio config->socketPath = NULL; break; } - else if(status=='H') + else if(status=='H') { - Trace("WaitForProfilerCompletion: Recieved health check ping from profiler"); + Trace("WaitForProfilerCompletion: Received health check ping from profiler"); + + if(config->NumberOfLeakReportsCollected == config->NumberOfDumpsToCollect) + { + // + // Since the protocol between profiler and procdump dictates when and how dumps are generated, + // we may still be in restrack only mode. In that case, we use the Health check call from the + // profiler to check if the number of leak reports that have been generated are at max. + // + Trace("WaitForProfilerCompletion: Total leak report count has been reached: %d", config->NumberOfLeakReportsCollected); + unlink(tmpFolder); + free(dump); + close(s2); + config->socketPath = NULL; + break; + } } free(dump); diff --git a/src/ProcDumpConfiguration.cpp b/src/ProcDumpConfiguration.cpp index 3071c20..d8ef6f6 100644 --- a/src/ProcDumpConfiguration.cpp +++ b/src/ProcDumpConfiguration.cpp @@ -152,6 +152,7 @@ void InitProcDumpConfiguration(struct ProcDumpConfiguration *self) self->bProcessGroup = false; self->ProcessGroup = NO_PID; self->NumberOfDumpsCollected = 0; + self->NumberOfLeakReportsCollected = 0; self->NumberOfDumpsToCollect = -1; self->CpuThreshold = -1; self->bCpuTriggerBelowValue = false; @@ -168,7 +169,7 @@ void InitProcDumpConfiguration(struct ProcDumpConfiguration *self) self->bMemoryTriggerBelowValue = false; self->bTimerThreshold = false; self->WaitingForProcessName = false; - self->DiagnosticsLoggingEnabled = false; + self->DiagnosticsLoggingEnabled = none; self->gcorePid = NO_PID; self->PollingInterval = -1; self->CoreDumpPath = NULL; @@ -179,6 +180,7 @@ void InitProcDumpConfiguration(struct ProcDumpConfiguration *self) self->ExceptionFilter = NULL; self->ExcludeFilter = NULL; self->bRestrackEnabled = false; + self->bRestrackGenerateDump = true; self->bLeakReportInProgress = false; self->SampleRate = 0; self->CoreDumpMask = -1; @@ -312,6 +314,7 @@ struct ProcDumpConfiguration * CopyProcDumpConfiguration(struct ProcDumpConfigur // copy runtime values from original config copy->NumberOfDumpsCollecting = self->NumberOfDumpsCollecting; copy->NumberOfDumpsCollected = self->NumberOfDumpsCollected; + copy->NumberOfLeakReportsCollected = self->NumberOfLeakReportsCollected; copy->bTerminated = self->bTerminated; // copy trigger behavior from original config @@ -342,6 +345,7 @@ struct ProcDumpConfiguration * CopyProcDumpConfiguration(struct ProcDumpConfigur } copy->bRestrackEnabled = self->bRestrackEnabled; + copy->bRestrackGenerateDump = self->bRestrackGenerateDump; copy->bLeakReportInProgress = self->bLeakReportInProgress; copy->SampleRate = self->SampleRate; copy->CoreDumpMask = self->CoreDumpMask; @@ -572,6 +576,17 @@ int GetOptions(struct ProcDumpConfiguration *self, int argc, char *argv[]) return PrintUsage(); } + if( i+1 >= argc) + { + return PrintUsage(); + } + + if(strcasecmp(argv[i+1], "nodump") == 0 ) + { + self->bRestrackGenerateDump = false; + i++; + } + self->bRestrackEnabled = true; } else if( 0 == strcasecmp( argv[i], "/sr" ) || @@ -695,7 +710,23 @@ int GetOptions(struct ProcDumpConfiguration *self, int argc, char *argv[]) else if( 0 == strcasecmp( argv[i], "/log" ) || 0 == strcasecmp( argv[i], "-log" )) { - self->DiagnosticsLoggingEnabled = true; + if( i+1 >= argc) return PrintUsage(); + + if( 0 == strcasecmp( argv[i+1], "stdout" ) ) + { + self->DiagnosticsLoggingEnabled = diag_stdout; + } + else if( 0 == strcasecmp( argv[i+1], "syslog" ) ) + { + self->DiagnosticsLoggingEnabled = diag_syslog; + } + else + { + Log(error, "Invalid diagnostics stream specified."); + return PrintUsage(); + } + + i++; } else if( 0 == strcasecmp( argv[i], "/e" ) || 0 == strcasecmp( argv[i], "-e" )) @@ -1215,7 +1246,7 @@ int PrintUsage() printf(" [-m|-ml Commit_Usage1[,Commit_Usage2...]]\n"); printf(" [-gcm [: | LOH: | POH:]Memory_Usage1[,Memory_Usage2...]]\n"); printf(" [-gcgen Generation]\n"); - printf(" [-restrack]\n"); + printf(" [-restrack [nodump]]\n"); printf(" [-sr Sample_Rate]\n"); printf(" [-tc Thread_Threshold]\n"); printf(" [-fc FileDescriptor_Threshold]\n"); @@ -1226,7 +1257,7 @@ int PrintUsage() printf(" [-mc Custom_Dump_Mask]\n"); printf(" [-pf Polling_Frequency]\n"); printf(" [-o]\n"); - printf(" [-log]\n"); + printf(" [-log syslog|stdout]\n"); printf(" {\n"); printf(" {{[-w] Process_Name | [-pgid] PID} [Dump_File | Dump_Folder]}\n"); printf(" }\n"); @@ -1240,7 +1271,7 @@ int PrintUsage() printf(" -ml Memory commit threshold(s) (MB) below which to create dumps.\n"); printf(" -gcm [.NET] GC memory threshold(s) (MB) above which to create dumps for the specified generation or heap (default is total .NET memory usage).\n"); printf(" -gcgen [.NET] Create dump when the garbage collection of the specified generation starts and finishes.\n"); - printf(" -restrack Enable memory leak tracking (malloc family of APIs).\n"); + printf(" -restrack Enable memory leak tracking (malloc family of APIs). Use the nodump option to prevent dump generation and only produce restrack report(s).\n"); printf(" -sr Sample rate when using -restrack.\n"); printf(" -tc Thread count threshold above which to create a dump of the process.\n"); printf(" -fc File descriptor count threshold above which to create a dump of the process.\n"); @@ -1251,7 +1282,7 @@ int PrintUsage() printf(" -mc Custom core dump mask (in hex) indicating what memory should be included in the core dump. Please see 'man core' (/proc/[pid]/coredump_filter) for available options.\n"); printf(" -pf Polling frequency.\n"); printf(" -o Overwrite existing dump file.\n"); - printf(" -log Writes extended ProcDump tracing to syslog.\n"); + printf(" -log Writes extended ProcDump tracing to the specified output stream (syslog or stdout).\n"); printf(" -w Wait for the specified process to launch if it's not running.\n"); printf(" -pgid Process ID specified refers to a process group ID.\n"); diff --git a/src/Procdump.cpp b/src/Procdump.cpp index 7241e88..f8b0db7 100644 --- a/src/Procdump.cpp +++ b/src/Procdump.cpp @@ -37,24 +37,15 @@ int main(int argc, char *argv[]) PrintBanner(); InitProcDump(); - // print privilege warning - if(geteuid() != 0) + // Parse command line arguments + if (GetOptions(&g_config, argc, argv) != 0) { - Log(warn, "Procdump not running with elevated credentials. If your uid does not match the uid of the target process procdump will not be able to capture memory dumps"); - PrintUsage(); + exit(-1); } - else - { - if (GetOptions(&g_config, argc, argv) != 0) - { - Trace("main: failed to parse command line arguments"); - exit(-1); - } - // Register exit handler - atexit(OnExit); + // Register exit handler + atexit(OnExit); - // monitor for all specified processes - MonitorProcesses(&g_config); - } + // monitor for all specified processes + MonitorProcesses(&g_config); } diff --git a/src/Process.cpp b/src/Process.cpp index f96ea81..82b33fa 100644 --- a/src/Process.cpp +++ b/src/Process.cpp @@ -7,25 +7,61 @@ // //-------------------------------------------------------------------- #include "Includes.h" +#include +#include +#include #include //-------------------------------------------------------------------- // -// GetProcessStat - Gets the process stats for the given pid +// GetUids - Gets the process uids for the given pid // //-------------------------------------------------------------------- -bool GetProcessStat(pid_t pid, struct ProcessStat *proc) { - char procFilePath[32]; - char fileBuffer[1024]; - char *token; - char *savePtr = NULL; - struct dirent* entry = NULL; +bool GetUids(pid_t pid, struct ProcessStat* proc) +{ - auto_free_file FILE *procFile = NULL; + std::ostringstream path; + path << "/proc/" << pid << "/status"; + + std::ifstream statusFile(path.str()); + if (!statusFile.is_open()) + { + Log(error, "Failed to open status file for pid: %d", pid); + return false; + } + + std::string line; + while (std::getline(statusFile, line)) + { + if (line.find("Uid:") == 0) + { + std::istringstream iss(line); + std::string uidTag, realUid, effectiveUid, savedUid, fsUid; + iss >> uidTag >> realUid >> effectiveUid >> savedUid >> fsUid; + proc->real_uid = std::stoi(realUid); + proc->effective_uid = std::stoi(effectiveUid); + proc->saved_uid = std::stoi(savedUid); + proc->fs_uid = std::stoi(fsUid); + return true; + } + } + + return false; +} + +//-------------------------------------------------------------------- +// +// GetNumFileDescriptors - Gets the process stats for the given pid +// +//-------------------------------------------------------------------- +bool GetNumFileDescriptors(pid_t pid, struct ProcessStat* proc) +{ auto_free_dir DIR* fddir = NULL; + struct dirent* entry = NULL; + char procFilePath[32]; - // Get number of file descriptors in /proc/%d/fdinfo. This directory only contains sub directories for each file descriptor. - if(sprintf(procFilePath, "/proc/%d/fdinfo", pid) < 0){ + if(sprintf(procFilePath, "/proc/%d/fdinfo", pid) < 0) + { return false; } @@ -40,13 +76,42 @@ bool GetProcessStat(pid_t pid, struct ProcessStat *proc) { } else { - Log(error, "Failed to open %s. Exiting...", procFilePath); + Log(error, "Failed to open %s [%s]", procFilePath, strerror(errno)); return false; } proc->num_filedescriptors-=2; // Account for "." and ".." + return true; +} + +//-------------------------------------------------------------------- +// +// GetProcessStat - Gets the process stats for the given pid +// +//-------------------------------------------------------------------- +bool GetProcessStat(pid_t pid, struct ProcessStat *proc) { + char procFilePath[32]; + char fileBuffer[1024]; + char *token; + char *savePtr = NULL; + + auto_free_file FILE *procFile = NULL; + + // Get UID's in /proc/%d/status + if(GetUids(pid, proc) == false) + { + Log(error, "Failed to get UID's"); + return false; + } + + // Get number of file descriptors in /proc/%d/fdinfo. This directory only contains sub directories for each file descriptor. + if(GetNumFileDescriptors(pid, proc) == false) + { + Log(error, "Failed to get number of file descriptors"); + return false; + } // Read /proc/[pid]/stat if(sprintf(procFilePath, "/proc/%d/stat", pid) < 0){ diff --git a/src/Restrack.cpp b/src/Restrack.cpp index 62ac13c..d645127 100644 --- a/src/Restrack.cpp +++ b/src/Restrack.cpp @@ -23,6 +23,7 @@ #include #include #include +#include typedef struct { unsigned int type; @@ -60,7 +61,7 @@ extern struct ProcDumpConfiguration g_config; // ------------------------------------------------------------------------------------------ static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) { - if(g_config.DiagnosticsLoggingEnabled == true) + if(g_config.DiagnosticsLoggingEnabled != none) { return vfprintf(stderr, format, args); } @@ -114,7 +115,7 @@ struct procdump_ebpf* RunRestrack(struct ProcDumpConfiguration *config) // // Setup extended error logging // - if(config->DiagnosticsLoggingEnabled == true) + if(config->DiagnosticsLoggingEnabled != none) { libbpf_set_print(libbpf_print_fn); } @@ -128,9 +129,26 @@ struct procdump_ebpf* RunRestrack(struct ProcDumpConfiguration *config) return skel; } + // + // Set eBPF program globals + // + std::string path = "/proc/" + std::to_string(config->ProcessId) + "/ns/pid"; + struct stat sb = {}; + if (stat(path.c_str(), &sb) == -1) + { + Trace("Failed to stat %s (%s)\n", path.c_str(), strerror(errno)); + return NULL; + } + + skel->bss->dev = sb.st_dev; + skel->bss->inode = sb.st_ino; skel->bss->target_PID = config->ProcessId; skel->bss->sampleRate = config->SampleRate; skel->bss->currentSampleCount = 1; + if(config->DiagnosticsLoggingEnabled != none) + { + skel->bss->isLoggingEnabled = true; + } ret = procdump_ebpf__load(skel); if (ret) @@ -176,7 +194,7 @@ int RestrackHandleEvent(void *ctx, void *data, size_t data_sz) activeConfigurations[event->pid]->memAllocMap[(uintptr_t) event->allocAddress] = event; pthread_mutex_unlock(&activeConfigurations[event->pid]->memAllocMapMutex); - if(activeConfigurations[event->pid]->DiagnosticsLoggingEnabled == true) + if(activeConfigurations[event->pid]->DiagnosticsLoggingEnabled != none) { Trace("Got event: Alloc size: %ld 0x%lx\n", event->allocSize, event->allocAddress); } @@ -199,7 +217,7 @@ int RestrackHandleEvent(void *ctx, void *data, size_t data_sz) activeConfigurations[event->pid]->memAllocMap.erase((uintptr_t) event->allocAddress); pthread_mutex_unlock(&activeConfigurations[event->pid]->memAllocMapMutex); - if(activeConfigurations[event->pid]->DiagnosticsLoggingEnabled == true) + if(activeConfigurations[event->pid]->DiagnosticsLoggingEnabled != none) { Trace("Got event: free 0x%lx\n", event->allocAddress); } @@ -544,15 +562,22 @@ void* ReportLeaks(void* args) // // Writes the raw stack trace (IPs only) to the specified file. // ------------------------------------------------------------------------------------------ -pthread_t WriteRestrackSnapshot(ProcDumpConfiguration* config, const char* filename) +pthread_t WriteRestrackSnapshot(ProcDumpConfiguration* config, ECoreDumpType type) { + // + // Generate the restrack filename + // + char* dumpFileName = GetCoreDumpName(config, type); + std::unique_ptr owndumpFileName(dumpFileName); + std::string filename = (std::string(dumpFileName) + ".restrack"); + // // Create a thread to write the snapshot to avoid delays in the calling thread. // Important due to symbol resolution possibly taking a longer time. // leakThreadArgs* args = (leakThreadArgs*) malloc(sizeof(leakThreadArgs)); args->config = config; - args->filename = strdup(filename); + args->filename = strdup(filename.c_str()); pthread_t thread = 0; int ret = pthread_create(&thread, NULL, ReportLeaks, args); if(ret != 0) @@ -561,5 +586,6 @@ pthread_t WriteRestrackSnapshot(ProcDumpConfiguration* config, const char* filen return 0; } + config->NumberOfLeakReportsCollected++; return thread; } diff --git a/tests/integration/run.sh b/tests/integration/run.sh index f0e47ec..fd1f7bd 100755 --- a/tests/integration/run.sh +++ b/tests/integration/run.sh @@ -1,5 +1,7 @@ #!/bin/bash +echo "You can optionally specify a specific test script to run rather than running all (default)" + failed=0 failedTests="\n" @@ -47,7 +49,13 @@ function runTest { for file in $DIR/scenarios/*.sh do - runTest $file + if [ ! -z "$1" ]; then + if [[ "$file" =~ "$1" ]]; then + runTest $file + fi + else + runTest $file + fi done printf "\nFailed tests: $failedTests" diff --git a/tests/integration/runProcDumpAndValidate.sh b/tests/integration/runProcDumpAndValidate.sh index 772aef2..672979d 100755 --- a/tests/integration/runProcDumpAndValidate.sh +++ b/tests/integration/runProcDumpAndValidate.sh @@ -38,9 +38,9 @@ function runProcDumpAndValidate { echo "ChildPID: $childpid" # We launch procdump in background and wait for 10 secs to complete the monitoring - echo "$PROCDUMPPATH -log $PREFIX $childpid $POSTFIX $dumpParam " + echo "$PROCDUMPPATH -log stdout $PREFIX $childpid $POSTFIX $dumpParam " echo [`date +"%T.%3N"`] Starting ProcDump - $PROCDUMPPATH -log $PREFIX $childpid $POSTFIX $dumpParam& + $PROCDUMPPATH -log stdout $PREFIX $childpid $POSTFIX $dumpParam& pidPD=$! echo "ProcDump PID: $pidPD" sleep 30s @@ -56,8 +56,8 @@ function runProcDumpAndValidate { else # We launch procdump in background and wait for target process to start echo [`date +"%T.%3N"`] Starting ProcDump - echo "$PROCDUMPPATH -log $PREFIX -w $TESTPROGNAME" $POSTFIX $dumpParam - $PROCDUMPPATH -log $PREFIX -w "$TESTPROGNAME" $POSTFIX $dumpParam& + echo "$PROCDUMPPATH -log stdout $PREFIX -w $TESTPROGNAME" $POSTFIX $dumpParam + $PROCDUMPPATH -log stdout $PREFIX -w "$TESTPROGNAME" $POSTFIX $dumpParam& pidPD=$! echo "ProcDump PID: $pidPD" diff --git a/tests/integration/scenarios/dotnet_0ExceptionDump1Thrown_dump.sh b/tests/integration/scenarios/dotnet_0ExceptionDump1Thrown_dump.sh index 74bb938..6421eba 100755 --- a/tests/integration/scenarios/dotnet_0ExceptionDump1Thrown_dump.sh +++ b/tests/integration/scenarios/dotnet_0ExceptionDump1Thrown_dump.sh @@ -22,7 +22,7 @@ if [ $? -eq -1 ]; then exit 1 fi -sudo $PROCDUMPPATH -log -e -w TestWebApi& +sudo $PROCDUMPPATH -log stdout -e -w TestWebApi& # waiting for procdump child process PROCDUMPCHILDPID=-1 diff --git a/tests/integration/scenarios/dotnet_0WildcardExceptionDump1Thrown_dump.sh b/tests/integration/scenarios/dotnet_0WildcardExceptionDump1Thrown_dump.sh index cbbdecf..8702a42 100755 --- a/tests/integration/scenarios/dotnet_0WildcardExceptionDump1Thrown_dump.sh +++ b/tests/integration/scenarios/dotnet_0WildcardExceptionDump1Thrown_dump.sh @@ -19,7 +19,7 @@ if [ $? -eq -1 ]; then exit 1 fi -sudo $PROCDUMPPATH -log -e -f invalid -w TestWebApi& +sudo $PROCDUMPPATH -log stdout -e -f invalid -w TestWebApi& # waiting for procdump child process PROCDUMPCHILDPID=-1 diff --git a/tests/integration/scenarios/dotnet_1FilterExceptionMsgDump1Thrown_dump.sh b/tests/integration/scenarios/dotnet_1FilterExceptionMsgDump1Thrown_dump.sh index 44e784a..903afd7 100755 --- a/tests/integration/scenarios/dotnet_1FilterExceptionMsgDump1Thrown_dump.sh +++ b/tests/integration/scenarios/dotnet_1FilterExceptionMsgDump1Thrown_dump.sh @@ -19,7 +19,7 @@ if [ $? -eq -1 ]; then exit 1 fi -sudo $PROCDUMPPATH -log -e -f "current state" -w TestWebApi& +sudo $PROCDUMPPATH -log stdout -e -f "current state" -w TestWebApi& # waiting for procdump child process PROCDUMPCHILDPID=-1 diff --git a/tests/integration/scenarios/dotnet_1WildcardExceptionDump1Thrown_dump.sh b/tests/integration/scenarios/dotnet_1WildcardExceptionDump1Thrown_dump.sh index bdfcb90..564aa70 100755 --- a/tests/integration/scenarios/dotnet_1WildcardExceptionDump1Thrown_dump.sh +++ b/tests/integration/scenarios/dotnet_1WildcardExceptionDump1Thrown_dump.sh @@ -19,7 +19,7 @@ if [ $? -eq -1 ]; then exit 1 fi -sudo $PROCDUMPPATH -log -e -f Invali*xception -w TestWebApi& +sudo $PROCDUMPPATH -log stdout -e -f Invali*xception -w TestWebApi& # waiting for procdump child process PROCDUMPCHILDPID=-1 diff --git a/tests/integration/scenarios/dotnet_1WildcardFilterExceptionMsgDump1Thrown_dump.sh b/tests/integration/scenarios/dotnet_1WildcardFilterExceptionMsgDump1Thrown_dump.sh index 16a8009..389eb43 100755 --- a/tests/integration/scenarios/dotnet_1WildcardFilterExceptionMsgDump1Thrown_dump.sh +++ b/tests/integration/scenarios/dotnet_1WildcardFilterExceptionMsgDump1Thrown_dump.sh @@ -19,7 +19,7 @@ if [ $? -eq -1 ]; then exit 1 fi -sudo $PROCDUMPPATH -log -e -f "*current*sta*" -w TestWebApi& +sudo $PROCDUMPPATH -log stdout -e -f "*current*sta*" -w TestWebApi& # waiting for procdump child process PROCDUMPCHILDPID=-1 diff --git a/tests/integration/scenarios/dotnet_1_gc_gen_1_dump.sh b/tests/integration/scenarios/dotnet_1_gc_gen_1_dump.sh index c776e11..4e770b6 100755 --- a/tests/integration/scenarios/dotnet_1_gc_gen_1_dump.sh +++ b/tests/integration/scenarios/dotnet_1_gc_gen_1_dump.sh @@ -20,7 +20,7 @@ if [ $? -eq -1 ]; then fi # Wait for 2 dumps (gc start and finish) for generation 1 -sudo $PROCDUMPPATH -log -gcgen 1 -w TestWebApi& +sudo $PROCDUMPPATH -log stdout -gcgen 1 -w TestWebApi& # waiting for procdump child process PROCDUMPCHILDPID=-1 diff --git a/tests/integration/scenarios/dotnet_1_gc_threshold_1_dump.sh b/tests/integration/scenarios/dotnet_1_gc_threshold_1_dump.sh index 6c7882b..d02f939 100755 --- a/tests/integration/scenarios/dotnet_1_gc_threshold_1_dump.sh +++ b/tests/integration/scenarios/dotnet_1_gc_threshold_1_dump.sh @@ -20,7 +20,7 @@ if [ $? -eq -1 ]; then fi # Wait for 1 GC commit thresholds (10MB) -sudo $PROCDUMPPATH -log -gcm 10 -w TestWebApi& +sudo $PROCDUMPPATH -log stdout -gcm 10 -w TestWebApi& # waiting for procdump child process PROCDUMPCHILDPID=-1 diff --git a/tests/integration/scenarios/dotnet_1exceptionDump1Thrown_dump.sh b/tests/integration/scenarios/dotnet_1exceptionDump1Thrown_dump.sh index ca7a28b..abed4ba 100755 --- a/tests/integration/scenarios/dotnet_1exceptionDump1Thrown_dump.sh +++ b/tests/integration/scenarios/dotnet_1exceptionDump1Thrown_dump.sh @@ -19,7 +19,7 @@ if [ $? -eq -1 ]; then exit 1 fi -sudo $PROCDUMPPATH -log -e -f System.InvalidOperationException -w TestWebApi& +sudo $PROCDUMPPATH -log stdout -e -f System.InvalidOperationException -w TestWebApi& # waiting for procdump child process PROCDUMPCHILDPID=-1 diff --git a/tests/integration/scenarios/dotnet_1exceptionDump2Thrown_dump.sh b/tests/integration/scenarios/dotnet_1exceptionDump2Thrown_dump.sh index b77acdc..e0b5911 100755 --- a/tests/integration/scenarios/dotnet_1exceptionDump2Thrown_dump.sh +++ b/tests/integration/scenarios/dotnet_1exceptionDump2Thrown_dump.sh @@ -19,7 +19,7 @@ if [ $? -eq -1 ]; then exit 1 fi -sudo $PROCDUMPPATH -log -e -f System.InvalidOperationException -w TestWebApi& +sudo $PROCDUMPPATH -log stdout -e -f System.InvalidOperationException -w TestWebApi& # waiting for procdump child process PROCDUMPCHILDPID=-1 diff --git a/tests/integration/scenarios/dotnet_2WildcardExceptionDump2Thrown_dump.sh b/tests/integration/scenarios/dotnet_2WildcardExceptionDump2Thrown_dump.sh index d917dd3..130a5f0 100755 --- a/tests/integration/scenarios/dotnet_2WildcardExceptionDump2Thrown_dump.sh +++ b/tests/integration/scenarios/dotnet_2WildcardExceptionDump2Thrown_dump.sh @@ -19,7 +19,7 @@ if [ $? -eq -1 ]; then exit 1 fi -sudo $PROCDUMPPATH -log -n 2 -e -f In*rat*ption -w TestWebApi& +sudo $PROCDUMPPATH -log stdout -n 2 -e -f In*rat*ption -w TestWebApi& # waiting for procdump child process PROCDUMPCHILDPID=-1 diff --git a/tests/integration/scenarios/dotnet_2exceptionDump2Thrown_dump.sh b/tests/integration/scenarios/dotnet_2exceptionDump2Thrown_dump.sh index 1916571..572fc03 100755 --- a/tests/integration/scenarios/dotnet_2exceptionDump2Thrown_dump.sh +++ b/tests/integration/scenarios/dotnet_2exceptionDump2Thrown_dump.sh @@ -19,7 +19,7 @@ if [ $? -eq -1 ]; then exit 1 fi -sudo $PROCDUMPPATH -log -n 2 -e -f System.InvalidOperationException -w TestWebApi& +sudo $PROCDUMPPATH -log stdout -n 2 -e -f System.InvalidOperationException -w TestWebApi& # waiting for procdump child process PROCDUMPCHILDPID=-1 diff --git a/tests/integration/scenarios/dotnet_3_gc_thresholds_3_dumps.sh b/tests/integration/scenarios/dotnet_3_gc_thresholds_3_dumps.sh index ed0927d..c9ce43a 100755 --- a/tests/integration/scenarios/dotnet_3_gc_thresholds_3_dumps.sh +++ b/tests/integration/scenarios/dotnet_3_gc_thresholds_3_dumps.sh @@ -20,7 +20,7 @@ if [ $? -eq -1 ]; then fi # Wait for 3 GC commit thresholds (10, 20 and 30MB) -sudo $PROCDUMPPATH -log -gcm 10,20,30 -w TestWebApi& +sudo $PROCDUMPPATH -log stdout -gcm 10,20,30 -w TestWebApi& # waiting for procdump child process PROCDUMPCHILDPID=-1 diff --git a/tests/integration/scenarios/dotnet_LOH_thresholds_3_dumps.sh b/tests/integration/scenarios/dotnet_LOH_thresholds_3_dumps.sh index 0b492e9..563fe29 100755 --- a/tests/integration/scenarios/dotnet_LOH_thresholds_3_dumps.sh +++ b/tests/integration/scenarios/dotnet_LOH_thresholds_3_dumps.sh @@ -20,7 +20,7 @@ if [ $? -eq -1 ]; then fi # Wait for 3 GC commit thresholds (10, 20 and 30MB) -sudo $PROCDUMPPATH -log -gcm LOH:10,20,30 -w TestWebApi& +sudo $PROCDUMPPATH -log stdout -gcm LOH:10,20,30 -w TestWebApi& # waiting for procdump child process PROCDUMPCHILDPID=-1 diff --git a/tests/integration/scenarios/dotnet_MemMultipleDumps.sh b/tests/integration/scenarios/dotnet_MemMultipleDumps.sh index 5b91276..d9944a1 100755 --- a/tests/integration/scenarios/dotnet_MemMultipleDumps.sh +++ b/tests/integration/scenarios/dotnet_MemMultipleDumps.sh @@ -19,7 +19,7 @@ if [ $? -eq -1 ]; then exit 1 fi -sudo $PROCDUMPPATH -log -m 10,20,30 -w TestWebApi& +sudo $PROCDUMPPATH -log stdout -m 10,20,30 -w TestWebApi& # waiting for procdump child process PROCDUMPCHILDPID=-1 diff --git a/tests/integration/scenarios/dotnet_POH_thresholds_3_dumps.sh b/tests/integration/scenarios/dotnet_POH_thresholds_3_dumps.sh index 0b578d8..1b1e553 100755 --- a/tests/integration/scenarios/dotnet_POH_thresholds_3_dumps.sh +++ b/tests/integration/scenarios/dotnet_POH_thresholds_3_dumps.sh @@ -20,7 +20,7 @@ if [ $? -eq -1 ]; then fi # Wait for 3 GC commit thresholds (10, 20 and 30MB) -sudo $PROCDUMPPATH -log -gcm POH:10,20,30 -w TestWebApi& +sudo $PROCDUMPPATH -log stdout -gcm POH:10,20,30 -w TestWebApi& # waiting for procdump child process PROCDUMPCHILDPID=-1 diff --git a/tests/integration/scenarios/dotnet_exception_notdump.sh b/tests/integration/scenarios/dotnet_exception_notdump.sh index 1584705..138ec2b 100755 --- a/tests/integration/scenarios/dotnet_exception_notdump.sh +++ b/tests/integration/scenarios/dotnet_exception_notdump.sh @@ -19,7 +19,7 @@ if [ $? -eq -1 ]; then exit 1 fi -sudo $PROCDUMPPATH -log -e -f System.NonExistingException -w TestWebApi& +sudo $PROCDUMPPATH -log stdout -e -f System.NonExistingException -w TestWebApi& # waiting for procdump child process PROCDUMPCHILDPID=-1 diff --git a/tests/integration/scenarios/dotnet_gen_2_thresholds_3_dumps.sh b/tests/integration/scenarios/dotnet_gen_2_thresholds_3_dumps.sh index 58057ff..7101f9a 100755 --- a/tests/integration/scenarios/dotnet_gen_2_thresholds_3_dumps.sh +++ b/tests/integration/scenarios/dotnet_gen_2_thresholds_3_dumps.sh @@ -20,7 +20,7 @@ if [ $? -eq -1 ]; then fi # Wait for 3 GC commit thresholds (10, 20 and 30MB) -sudo $PROCDUMPPATH -log -gcm 2:4,6,8 -w TestWebApi& +sudo $PROCDUMPPATH -log stdout -gcm 2:4,6,8 -w TestWebApi& # waiting for procdump child process PROCDUMPCHILDPID=-1 diff --git a/tests/integration/scenarios/dotnet_sigint_procdump.sh b/tests/integration/scenarios/dotnet_sigint_procdump.sh index bfd34c9..114567e 100755 --- a/tests/integration/scenarios/dotnet_sigint_procdump.sh +++ b/tests/integration/scenarios/dotnet_sigint_procdump.sh @@ -19,7 +19,7 @@ if [ $? -eq -1 ]; then exit 1 fi -sudo $PROCDUMPPATH -log -e TestWebApi& +sudo $PROCDUMPPATH -log stdout -e TestWebApi& # waiting for procdump child process PROCDUMPCHILDPID=-1 diff --git a/tests/integration/scenarios/dotnet_wildcardexception_notdump.sh b/tests/integration/scenarios/dotnet_wildcardexception_notdump.sh index f9e6ba6..9ddf35f 100755 --- a/tests/integration/scenarios/dotnet_wildcardexception_notdump.sh +++ b/tests/integration/scenarios/dotnet_wildcardexception_notdump.sh @@ -19,7 +19,7 @@ if [ $? -eq -1 ]; then exit 1 fi -sudo $PROCDUMPPATH -log -e -f on*Existing -w TestWebApi& +sudo $PROCDUMPPATH -log stdout -e -f on*Existing -w TestWebApi& # waiting for procdump child process PROCDUMPCHILDPID=-1