Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 55 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ set(profiler_INC ${CMAKE_SOURCE_DIR}/profiler/inc)
set(profiler_SRC ${CMAKE_SOURCE_DIR}/profiler/src)
set(procdump_INC ${CMAKE_SOURCE_DIR}/include)
set(procdump_SRC ${CMAKE_SOURCE_DIR}/src)
set(lib_INC ${CMAKE_SOURCE_DIR}/lib)
set(lib_SRC ${CMAKE_SOURCE_DIR}/lib)
set(procdump_Test ${CMAKE_SOURCE_DIR}/tests/integration)
set(LD "/usr/bin/ld")
set(libbpf_SOURCE_DIR ${CMAKE_BINARY_DIR}/libbpf/src/libbpf)
Expand Down Expand Up @@ -192,7 +194,12 @@ endif()

if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
message(STATUS "Building for Linux")
add_executable(procdump

# Build the ProcDump core dump engine as a static library (libprocdump.a).
# External programs can link against this library and call the on-demand
# dump API declared in lib/ProcDumpLib.h. The procdump executable is a thin
# command-line front-end on top of the same library.
add_library(procdumplib STATIC
${procdump_SRC}/CoreDumpWriter.cpp
${procdump_SRC}/DotnetHelpers.cpp
${procdump_SRC}/Events.cpp
Expand All @@ -201,18 +208,24 @@ if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
${procdump_SRC}/Handle.cpp
${procdump_SRC}/Logging.cpp
${procdump_SRC}/Monitor.cpp
${procdump_SRC}/Procdump.cpp
${procdump_SRC}/ProcDumpConfiguration.cpp
${procdump_SRC}/Process.cpp
${procdump_SRC}/ProfilerHelpers.cpp
${procdump_SRC}/Restrack.cpp
${lib_SRC}/ProcDumpLib.cpp
${sym_SOURCE_DIR}/bcc_proc.cpp
${sym_SOURCE_DIR}/bcc_syms.cc
${sym_SOURCE_DIR}/bcc_elf.cpp
${sym_SOURCE_DIR}/bcc_perf_map.cpp
${sym_SOURCE_DIR}/bcc_zip.cpp
${PROJECT_BINARY_DIR}/ProcDumpProfiler.o
)
# Produce libprocdump.a (strip the default "libprocdumplib" naming).
set_target_properties(procdumplib PROPERTIES OUTPUT_NAME procdump)

add_executable(procdump
${procdump_SRC}/Procdump.cpp
)

# Build corex as a separate static C library
add_library(corex STATIC
Expand All @@ -225,6 +238,21 @@ if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
)
target_include_directories(corex PRIVATE ${corex_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/include)
target_compile_options(corex PRIVATE -g -pthread -fstack-protector-all -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -Werror -D_GNU_SOURCE -O2)

target_compile_options(procdumplib PRIVATE -g -pthread -fstack-protector-all -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -Werror -D_GNU_SOURCE -std=c++11 -O2)
target_include_directories(procdumplib PUBLIC
${procdump_INC}
${lib_INC}
${PROJECT_BINARY_DIR}
/usr/include
${sym_SOURCE_DIR}
${procdump_ebpf_SOURCE_DIR}
${corex_SOURCE_DIR}
)
if(NOT APPLE AND CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64")
target_include_directories(procdumplib PUBLIC /usr/include/aarch64-linux-gnu)
endif()
add_dependencies(procdumplib libbpf procdump_ebpf)
else()
add_executable(procdump
${procdump_SRC}/CoreDumpWriter.cpp
Expand Down Expand Up @@ -252,6 +280,7 @@ target_compile_options(procdump PRIVATE -g -pthread -fstack-protector-all -U_FOR

target_include_directories(procdump PUBLIC
${procdump_INC}
${lib_INC}
${PROJECT_BINARY_DIR}
/usr/include
${sym_SOURCE_DIR}
Expand All @@ -268,7 +297,8 @@ endif()

if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
add_dependencies(procdump libbpf procdump_ebpf)
target_link_libraries(procdump ${libbpf_SOURCE_DIR}/src/libbpf.a elf z pthread corex)
target_link_libraries(procdumplib PUBLIC ${libbpf_SOURCE_DIR}/src/libbpf.a elf z pthread corex)
target_link_libraries(procdump procdumplib)
else()
target_link_libraries(procdump z pthread)
endif()
Expand Down Expand Up @@ -297,6 +327,28 @@ target_include_directories(ProcDumpTestApplication PUBLIC

target_link_libraries(ProcDumpTestApplication pthread)

#
# Make ProcDump library test driver
#
# Thin CLI that exercises the public on-demand dump API (pdWriteDump /
# pdFreeError) declared in lib/ProcDumpLib.h by linking against libprocdump.a.
# Used by the integration test scenarios (lib_api_*.sh) to validate the library.
#
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
add_executable(ProcDumpLibTestDriver
${procdump_Test}/ProcDumpLibTestDriver.cpp
)

target_compile_options(ProcDumpLibTestDriver PRIVATE -g -pthread -fstack-protector-all -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -Werror -D_GNU_SOURCE -std=c++11 -O2)

target_include_directories(ProcDumpLibTestDriver PUBLIC
${lib_INC}
)

add_dependencies(ProcDumpLibTestDriver procdumplib)
target_link_libraries(ProcDumpLibTestDriver procdumplib)
endif()

#
# Make package(s)
#
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# ProcDump [![Build Status](https://dev.azure.com/sysinternals/Tools/_apis/build/status/Sysinternals.ProcDump-for-Linux?branchName=master)](https://dev.azure.com/sysinternals/Tools/_build/latest?definitionId=341&branchName=master)
ProcDump is a Linux and Mac reimagining of the classic ProcDump tool from the Sysinternals suite of tools for Windows. ProcDump provides a convenient way for Linux and Mac developers to create core dumps of their application based on performance triggers. ProcDump for Linux and Mac is part of [Sysinternals](https://sysinternals.com).
# Sysinternals ProcDump [![Build Status](https://dev.azure.com/sysinternals/Tools/_apis/build/status/Sysinternals.ProcDump-for-Linux?branchName=master)](https://dev.azure.com/sysinternals/Tools/_build/latest?definitionId=341&branchName=master)
ProcDump is a Linux and Mac reimagining of the classic Sysinternals ProcDump tool. ProcDump provides a convenient way for Linux and Mac developers to create core dumps of their application based on performance triggers. ProcDump for Linux and Mac is part of [Sysinternals](https://sysinternals.com).

![ProcDump in use](procdump.gif "Procdump in use")

Expand Down
1 change: 1 addition & 0 deletions include/CoreDumpWriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ enum ECoreDumpType {
struct CoreDumpWriter {
struct ProcDumpConfiguration *Config;
enum ECoreDumpType Type;
char *ErrorMessage; // Set to a descriptive, caller-owned string when dump generation fails (else NULL)
};

struct CoreDumpWriter *NewCoreDumpWriter(enum ECoreDumpType type, struct ProcDumpConfiguration *config);
Expand Down
143 changes: 143 additions & 0 deletions lib/ProcDumpLib.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License

//--------------------------------------------------------------------
//
// ProcDumpLib - Implementation of the public on-demand dump API.
//
// This wraps ProcDump's existing core dump engine (CoreDumpWriter) behind
// a small, dependency-free C API so that other programs can link against
// libprocdump.a and generate a dump without spawning the procdump tool.
//
//--------------------------------------------------------------------

#include "Includes.h"
#include "ProcDumpLib.h"

#include <libgen.h>

//--------------------------------------------------------------------
//
// pdWriteDump - Immediately generate a core dump of the target process.
//
//--------------------------------------------------------------------
extern "C" int pdWriteDump(
pid_t processId,
const char* dumpPath,
int dumpMask,
bool bOverwrite,
char** error)
{
if(error != NULL)
{
*error = NULL;
}

if(processId <= 0 || dumpPath == NULL || dumpPath[0] == '\0')
{
if(error != NULL)
{
*error = strdup("Invalid argument: a valid processId and dumpPath are required.");
}
return -1;
}

//
// The existing writer constructs the final dump name from a directory
// (CoreDumpPath) plus a file name prefix (CoreDumpName). Split the caller
// supplied full path accordingly. dirname()/basename() may modify their
// argument, so operate on copies.
//
char* dirCopy = strdup(dumpPath);
char* baseCopy = strdup(dumpPath);
if(dirCopy == NULL || baseCopy == NULL)
{
free(dirCopy);
free(baseCopy);
if(error != NULL)
{
*error = strdup("Out of memory.");
}
return -1;
}

char* dir = dirname(dirCopy);
char* base = basename(baseCopy);

//
// Build a minimal configuration sufficient to generate a single dump.
// Value-initialize so every POD field defaults to 0/false (matching the
// zero-initialized global config the procdump tool relies on); a few
// fields read during monitoring (e.g. bTerminated) are not set by
// InitProcDumpConfiguration.
//
struct ProcDumpConfiguration config = {};
InitProcDumpConfiguration(&config);

config.ProcessId = processId;
config.ProcessName = GetProcessName(processId);
if(config.ProcessName == NULL)
{
config.ProcessName = strdup("process");
}
config.CoreDumpPath = strdup(dir);
config.CoreDumpName = strdup(base);
config.NumberOfDumpsToCollect = 1;
config.NumberOfDumpsCollected = 0;
config.bOverwriteExisting = bOverwrite;
config.CoreDumpMask = dumpMask;
config.bUseGcore = false;
config.nQuit = 0;
config.bTerminated = false;

free(dirCopy);
free(baseCopy);

int ret = 0;
struct CoreDumpWriter* writer = NewCoreDumpWriter(MANUAL, &config);

char* dumpName = WriteCoreDump(writer);
if(dumpName == NULL)
{
ret = -1;
if(error != NULL)
{
if(writer->ErrorMessage != NULL)
{
*error = strdup(writer->ErrorMessage);
}
else
{
*error = strdup("Failed to generate core dump.");
}
}
}
else
{
free(dumpName);
}

if(writer->ErrorMessage != NULL)
{
free(writer->ErrorMessage);
}
free(writer);

// FreeProcDumpConfiguration releases ProcessName, CoreDumpPath and CoreDumpName.
FreeProcDumpConfiguration(&config);

return ret;
}

//--------------------------------------------------------------------
//
// pdFreeError - Free an error string returned by pdWriteDump.
//
//--------------------------------------------------------------------
extern "C" void pdFreeError(char* error)
{
if(error != NULL)
{
free(error);
}
}
68 changes: 68 additions & 0 deletions lib/ProcDumpLib.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License

//--------------------------------------------------------------------
//
// ProcDumpLib - Public API for on-demand core dump generation.
//
// Link against libprocdump.a and include this header to generate a core
// dump of a target process programmatically (without running the procdump
// command-line tool).
//
//--------------------------------------------------------------------

#ifndef PROCDUMP_LIB_H
#define PROCDUMP_LIB_H

#include <sys/types.h> // pid_t
#include <stdbool.h>

#ifdef __cplusplus
extern "C" {
#endif

//
// Pass as 'dumpMask' to leave the target process' coredump_filter unchanged.
//
#define PD_DUMP_MASK_DEFAULT (-1)

//---------------------------------------------------------------------------------------------------------
// pdWriteDump
//
// Immediately generates a core dump of the target process.
//
// processId - Process ID of the target process.
// dumpPath - Full path of the resulting dump file. On Linux the file that is actually produced
// has ".<pid>" appended to this path (consistent with the procdump tool).
// dumpMask - Linux coredump_filter bitmask to apply while generating the dump, or
// PD_DUMP_MASK_DEFAULT (-1) to leave the process' existing filter unchanged.
// See https://man7.org/linux/man-pages/man5/core.5.html (coredump_filter) for bit meanings.
// bOverwrite - If true, overwrite an existing dump file of the same name; otherwise the call fails
// if the dump file already exists.
// error - Optional. If non-NULL and the function fails, receives a newly allocated, NUL-terminated
// UTF-8 error string that the caller must release with pdFreeError(). Set to NULL on success.
//
// Returns 0 on success, non-zero on failure.
//---------------------------------------------------------------------------------------------------------
int pdWriteDump(
pid_t processId,
const char* dumpPath,
int dumpMask,
bool bOverwrite,
char** error);

//---------------------------------------------------------------------------------------------------------
// pdFreeError
//
// Frees an error string returned by pdWriteDump.
//
// error - Pointer to the error string to free. May be NULL.
//---------------------------------------------------------------------------------------------------------
void pdFreeError(
char* error);

#ifdef __cplusplus
}
#endif

#endif // PROCDUMP_LIB_H
43 changes: 43 additions & 0 deletions lib/sample.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License

//--------------------------------------------------------------------
//
// Sample program demonstrating the ProcDump on-demand dump API.
//
// Build (from the build directory after `make`):
// clang++ -I../lib ../lib/sample.cpp libprocdump.a libcorex.a \
// libbpf/src/libbpf/src/libbpf.a -lelf -lz -lpthread -o pd_sample
//
// Usage:
// ./pd_sample <pid> <dump-path-prefix>
//
//--------------------------------------------------------------------

#include "ProcDumpLib.h"
#include <cstdio>
#include <cstdlib>

int main(int argc, char* argv[])
{
if(argc < 3)
{
fprintf(stderr, "Usage: %s <pid> <dump-path-prefix>\n", argv[0]);
return 2;
}

pid_t pid = (pid_t)atoi(argv[1]);
const char* path = argv[2];

char* error = NULL;
int rc = pdWriteDump(pid, path, PD_DUMP_MASK_DEFAULT, /*bOverwrite*/ true, &error);
if(rc != 0)
{
fprintf(stderr, "pdWriteDump failed: %s\n", error ? error : "(no detail)");
pdFreeError(error);
return 1;
}

printf("Dump generated successfully (prefix: %s)\n", path);
return 0;
}
Loading