From 679b86b795b8b9c53cfb74f774646a3fa90a0a44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Fri, 9 Apr 2021 08:41:18 +0200 Subject: [PATCH 01/49] feat: Add support for Qt 6 (#509) --- CMakeLists.txt | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2407fca86..fe88796f9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -460,8 +460,17 @@ endif() option(SENTRY_INTEGRATION_QT "Build Qt integration") if(SENTRY_INTEGRATION_QT) - find_package(Qt5 COMPONENTS Core REQUIRED) - target_link_libraries(sentry PRIVATE Qt5::Core) + if(QT_DEFAULT_MAJOR_VERSION) + # Let user choose major version + set(Qt_VERSION_MAJOR ${QT_DEFAULT_MAJOR_VERSION}) + else() + # Find best match, prioritizing Qt 6 if available + find_package(Qt NAMES Qt6 Qt5 COMPONENTS Core REQUIRED) + endif() + find_package(Qt${Qt_VERSION_MAJOR} COMPONENTS Core REQUIRED) + message(STATUS "Found Qt: ${Qt${Qt_VERSION_MAJOR}_DIR} " + "(found version \"${Qt${Qt_VERSION_MAJOR}_VERSION}\")") + target_link_libraries(sentry PRIVATE Qt${Qt_VERSION_MAJOR}::Core) endif() include(CMakePackageConfigHelpers) From 43c8d9d056b2175703c8ed5b34b6f2d8bee73dbd Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Fri, 9 Apr 2021 09:55:32 +0200 Subject: [PATCH 02/49] fix: Windows SDK Compiler Warning (#511) --- tests/unit/CMakeLists.txt | 4 ++++ tests/unit/fuzz.c | 9 ++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 1ed5ffd7b..adecacdfa 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -98,4 +98,8 @@ target_link_libraries(sentry_fuzz_json PRIVATE "$<$:rt>" ) +if(MSVC) + target_compile_options(sentry_fuzz_json PRIVATE $) +endif() + add_test(NAME sentry_fuzz_json COMMAND sentry_fuzz_json) diff --git a/tests/unit/fuzz.c b/tests/unit/fuzz.c index 0ad1afd2d..1b79da7e5 100644 --- a/tests/unit/fuzz.c +++ b/tests/unit/fuzz.c @@ -18,10 +18,17 @@ afl-fuzz -i fuzzing-examples -o fuzzing-results -- fuzzing/sentry_fuzz_json @@ #undef NDEBUG +#ifdef _WIN32 +# define WIN32_LEAN_AND_MEAN +# define NOMINMAX +# define _CRT_SECURE_NO_WARNINGS +#endif + +#include "sentry.h" + #include #include -#include "sentry.h" #include "sentry_json.h" #include "sentry_path.h" #include "sentry_value.h" From babae96b9ac71420f9eea4abae8ce333815a7c90 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Fri, 9 Apr 2021 11:40:54 +0200 Subject: [PATCH 03/49] fix: Validate tm put into strftime (#510) --- src/sentry_utils.c | 6 ++++++ src/sentry_value.c | 3 +++ 2 files changed, 9 insertions(+) diff --git a/src/sentry_utils.c b/src/sentry_utils.c index c91d406e0..5134b56c9 100644 --- a/src/sentry_utils.c +++ b/src/sentry_utils.c @@ -367,6 +367,12 @@ sentry__msec_time_to_iso8601(uint64_t time) struct tm tm_buf; tm = gmtime_r(&secs, &tm_buf); #endif + // It might as well be that the `time` parameter is broken in some way and + // would create a broken `tm` that then later causes formatting issues. We + // have seen super strange timestamps in some event payloads. + if (!tm || tm->tm_year > 9000) { + return NULL; + } size_t written = strftime(buf, buf_len, "%Y-%m-%dT%H:%M:%S", tm); if (written == 0) { return NULL; diff --git a/src/sentry_value.c b/src/sentry_value.c index 5bed6d1bc..1e4b72525 100644 --- a/src/sentry_value.c +++ b/src/sentry_value.c @@ -911,6 +911,9 @@ sentry_value_to_msgpack(sentry_value_t value, size_t *size_out) sentry_value_t sentry__value_new_string_owned(char *s) { + if (!s) { + return sentry_value_new_null(); + } sentry_value_t rv = new_thing_value(s, THING_TYPE_STRING | THING_TYPE_FROZEN); if (sentry_value_is_null(rv)) { From 03ebec7fc26108b7baf058e40691463918fa89d0 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Fri, 9 Apr 2021 14:49:47 +0200 Subject: [PATCH 04/49] fix: Rewrite the Linux module finder (#431) It now works in memory, without requiring to mmap the libraries again, which should make it work correctly on android when loading libraries directly from apk or appbundle files. The new code will keep track of readable memory maps and will translate read requests based on the offset of those memory maps, making sure that we actually can read whatever we are trying to read. --- src/modulefinder/sentry_modulefinder_linux.c | 403 ++++++++++--------- src/modulefinder/sentry_modulefinder_linux.h | 43 +- tests/unit/test_modulefinder.c | 88 ++-- tests/unit/tests.inc | 1 + tests/valgrind.txt | 29 ++ 5 files changed, 339 insertions(+), 225 deletions(-) diff --git a/src/modulefinder/sentry_modulefinder_linux.c b/src/modulefinder/sentry_modulefinder_linux.c index ffe0f170d..513c22165 100644 --- a/src/modulefinder/sentry_modulefinder_linux.c +++ b/src/modulefinder/sentry_modulefinder_linux.c @@ -10,37 +10,107 @@ #include #include #include -#include -#include #include #include -#define MAX(a, b) ((a) > (b) ? (a) : (b)) #define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define ENSURE(Ptr) \ + if (!Ptr) \ + goto fail + static bool g_initialized = false; static sentry_mutex_t g_mutex = SENTRY__MUTEX_INIT; static sentry_value_t g_modules = { 0 }; static sentry_slice_t LINUX_GATE = { "linux-gate.so", 13 }; +/** + * Checks that `start_offset` + `size` is a valid contiguous mapping in the + * mapped regions, and returns the translated pointer corresponding to + * `start_offset`. + */ +void * +sentry__module_get_addr( + const sentry_module_t *module, uint64_t start_offset, uint64_t size) +{ + for (size_t i = 0; i < module->num_mappings; i++) { + const sentry_mapped_region_t *mapping = &module->mappings[i]; + uint64_t mapping_offset = mapping->offset - module->offset_in_inode; + + // start_offset is inside this mapping + if (start_offset >= mapping_offset + && start_offset < mapping_offset + mapping->size) { + uint64_t addr = start_offset - mapping_offset + mapping->addr; + // the requested size is fully inside the mapping + if (addr + size <= mapping->addr + mapping->size) { + return (void *)(uintptr_t)(addr); + } + } + } + return NULL; +} + +static void +sentry__module_mapping_push( + sentry_module_t *module, const sentry_parsed_module_t *parsed) +{ + if (module->num_mappings && module->mappings_inode != parsed->inode) { + return; + } + + size_t size = parsed->end - parsed->start; + if (module->num_mappings) { + sentry_mapped_region_t *last_mapping + = &module->mappings[module->num_mappings - 1]; + if (last_mapping->addr + last_mapping->size == parsed->start + && last_mapping->offset + last_mapping->size == parsed->offset) { + last_mapping->size += size; + return; + } + } + if (module->num_mappings < SENTRY_MAX_MAPPINGS) { + sentry_mapped_region_t *mapping + = &module->mappings[module->num_mappings]; + module->num_mappings += 1; + mapping->offset = parsed->offset; + mapping->size = size; + mapping->addr = parsed->start; + + if (module->num_mappings == 1) { + module->mappings_inode = parsed->inode; + module->offset_in_inode = parsed->offset; + } + } +} + +static bool +is_duplicated_mapping( + const sentry_module_t *module, const sentry_parsed_module_t *parsed) +{ + if (!module->num_mappings) { + return false; + } + const sentry_mapped_region_t *mapping = &module->mappings[0]; + return (mapping->offset == parsed->offset + && module->mappings_inode == parsed->inode); +} + int -sentry__procmaps_parse_module_line(const char *line, sentry_module_t *module) +sentry__procmaps_parse_module_line( + const char *line, sentry_parsed_module_t *module) { - char permissions[5] = { 0 }; - uint64_t offset; uint8_t major_device; uint8_t minor_device; - uint64_t inode; int consumed = 0; // this has been copied from the breakpad source: // https://github.com/google/breakpad/blob/13c1568702e8804bc3ebcfbb435a2786a3e335cf/src/processor/proc_maps_linux.cc#L66 if (sscanf(line, - "%" SCNxPTR "-%" SCNxPTR " %4c %" SCNx64 " %hhx:%hhx %" SCNu64 - " %n", - (uintptr_t *)&module->start, (uintptr_t *)&module->end, permissions, - &offset, &major_device, &minor_device, &inode, &consumed) + "%" SCNx64 "-%" SCNx64 " %4c %" SCNx64 " %hhx:%hhx %" SCNu64 " %n", + &module->start, &module->end, &module->permissions[0], + &module->offset, &major_device, &minor_device, &module->inode, + &consumed) < 7) { return 0; } @@ -66,53 +136,6 @@ sentry__procmaps_parse_module_line(const char *line, sentry_module_t *module) return consumed; } -bool -sentry__mmap_file(sentry_mmap_t *rv, const char *path) -{ - rv->fd = open(path, O_RDONLY); - if (rv->fd < 0) { - goto fail; - } - - struct stat sb; - if (stat(path, &sb) != 0 || !S_ISREG(sb.st_mode)) { - goto fail; - } - - rv->len = sb.st_size; - if (rv->len == 0) { - goto fail; - } - - rv->ptr = mmap(NULL, rv->len, PROT_READ, MAP_PRIVATE, rv->fd, 0); - if (rv->ptr == MAP_FAILED) { - goto fail; - } - - return true; - -fail: - if (rv->fd > 0) { - close(rv->fd); - } - rv->fd = 0; - rv->ptr = NULL; - rv->len = 0; - return false; - - return rv; -} - -void -sentry__mmap_close(sentry_mmap_t *m) -{ - munmap(m->ptr, m->len); - close(m->fd); - m->ptr = NULL; - m->len = 0; - m->fd = 0; -} - void align(size_t alignment, void **offset) { @@ -135,7 +158,7 @@ get_code_id_from_notes( const uint8_t *offset = start; while (offset < (const uint8_t *)end) { - // The note header size is independant of the architecture, so we just + // The note header size is independent of the architecture, so we just // use the `Elf64_Nhdr` variant. const Elf64_Nhdr *note = (const Elf64_Nhdr *)offset; // the headers are consecutive, and the optional `name` and `desc` are @@ -154,106 +177,122 @@ get_code_id_from_notes( return NULL; } -static bool -is_elf_module(void *addr) -{ - // we try to interpret `addr` as an ELF file, which should start with a - // magic number... - const unsigned char *e_ident = addr; - return e_ident[EI_MAG0] == ELFMAG0 && e_ident[EI_MAG1] == ELFMAG1 - && e_ident[EI_MAG2] == ELFMAG2 && e_ident[EI_MAG3] == ELFMAG3; -} - static const uint8_t * -get_code_id_from_elf(void *base, size_t *size_out) +get_code_id_from_elf(const sentry_module_t *module, size_t *size_out) { *size_out = 0; - // now this is interesting: - // `p_offset` is defined as the offset of the section relative to the file, - // and `p_vaddr` is supposed to be the memory location. - // interestingly though, when compiled with gcc 7.4, both are the same, - // because apparently it does not really patch up the `p_vaddr`. gcc 5.4 - // however does, so `p_vaddr` is an actual pointer, and not an offset to - // be added to the `base`. So we are using `p_offset` here, since it seems - // to be the correct offset relative to `base` using both compilers. - const uint8_t *addr = base; - // iterate over all the program headers, for 32/64 bit separately - const unsigned char *e_ident = addr; + const unsigned char *e_ident + = sentry__module_get_addr(module, 0, EI_NIDENT); + ENSURE(e_ident); if (e_ident[EI_CLASS] == ELFCLASS64) { - const Elf64_Ehdr *elf = base; + const Elf64_Ehdr *elf + = sentry__module_get_addr(module, 0, sizeof(Elf64_Ehdr)); + ENSURE(elf); for (int i = 0; i < elf->e_phnum; i++) { - const Elf64_Phdr *header = (const Elf64_Phdr *)(addr + elf->e_phoff - + elf->e_phentsize * i); + const Elf64_Phdr *header = sentry__module_get_addr( + module, elf->e_phoff + elf->e_phentsize * i, elf->e_phentsize); + ENSURE(header); + // we are only interested in notes if (header->p_type != PT_NOTE) { continue; } + void *segment_addr = sentry__module_get_addr( + module, header->p_offset, header->p_filesz); + ENSURE(segment_addr); const uint8_t *code_id = get_code_id_from_notes(header->p_align, - (void *)(addr + header->p_offset), - (void *)(addr + header->p_offset + header->p_memsz), size_out); + segment_addr, + (void *)((uintptr_t)segment_addr + header->p_filesz), size_out); if (code_id) { return code_id; } } } else { - const Elf32_Ehdr *elf = base; + const Elf32_Ehdr *elf + = sentry__module_get_addr(module, 0, sizeof(Elf32_Ehdr)); + ENSURE(elf); + for (int i = 0; i < elf->e_phnum; i++) { - const Elf32_Phdr *header = (const Elf32_Phdr *)(addr + elf->e_phoff - + elf->e_phentsize * i); + const Elf32_Phdr *header = sentry__module_get_addr( + module, elf->e_phoff + elf->e_phentsize * i, elf->e_phentsize); + ENSURE(header); // we are only interested in notes if (header->p_type != PT_NOTE) { continue; } + void *segment_addr = sentry__module_get_addr( + module, header->p_offset, header->p_filesz); + ENSURE(segment_addr); const uint8_t *code_id = get_code_id_from_notes(header->p_align, - (void *)(addr + header->p_offset), - (void *)(addr + header->p_offset + header->p_memsz), size_out); + segment_addr, + (void *)((uintptr_t)segment_addr + header->p_filesz), size_out); if (code_id) { return code_id; } } } +fail: return NULL; } static sentry_uuid_t -get_code_id_from_text_fallback(void *base) +get_code_id_from_text_fallback(const sentry_module_t *module) { const uint8_t *text = NULL; size_t text_size = 0; - const uint8_t *addr = base; // iterate over all the program headers, for 32/64 bit separately - const unsigned char *e_ident = addr; + const unsigned char *e_ident + = sentry__module_get_addr(module, 0, EI_NIDENT); + ENSURE(e_ident); if (e_ident[EI_CLASS] == ELFCLASS64) { - const Elf64_Ehdr *elf = base; - const Elf64_Shdr *strheader = (const Elf64_Shdr *)(addr + elf->e_shoff - + elf->e_shentsize * elf->e_shstrndx); - - const char *names = (const char *)(addr + strheader->sh_offset); + const Elf64_Ehdr *elf + = sentry__module_get_addr(module, 0, sizeof(Elf64_Ehdr)); + ENSURE(elf); + const Elf64_Shdr *strheader = sentry__module_get_addr(module, + elf->e_shoff + elf->e_shentsize * elf->e_shstrndx, + elf->e_shentsize); + ENSURE(strheader); + + const char *names = sentry__module_get_addr( + module, strheader->sh_offset, strheader->sh_entsize); + ENSURE(names); for (int i = 0; i < elf->e_shnum; i++) { - const Elf64_Shdr *header = (const Elf64_Shdr *)(addr + elf->e_shoff - + elf->e_shentsize * i); + const Elf64_Shdr *header = sentry__module_get_addr( + module, elf->e_shoff + elf->e_shentsize * i, elf->e_shentsize); + ENSURE(header); const char *name = names + header->sh_name; if (header->sh_type == SHT_PROGBITS && strcmp(name, ".text") == 0) { - text = addr + header->sh_offset; + text = sentry__module_get_addr( + module, header->sh_offset, header->sh_size); + ENSURE(text); text_size = header->sh_size; break; } } } else { - const Elf32_Ehdr *elf = base; - const Elf32_Shdr *strheader = (const Elf32_Shdr *)(addr + elf->e_shoff - + elf->e_shentsize * elf->e_shstrndx); - - const char *names = (const char *)(addr + strheader->sh_offset); + const Elf32_Ehdr *elf + = sentry__module_get_addr(module, 0, sizeof(Elf64_Ehdr)); + ENSURE(elf); + const Elf32_Shdr *strheader = sentry__module_get_addr(module, + elf->e_shoff + elf->e_shentsize * elf->e_shstrndx, + elf->e_shentsize); + ENSURE(strheader); + + const char *names = sentry__module_get_addr( + module, strheader->sh_offset, strheader->sh_entsize); + ENSURE(names); for (int i = 0; i < elf->e_shnum; i++) { - const Elf32_Shdr *header = (const Elf32_Shdr *)(addr + elf->e_shoff - + elf->e_shentsize * i); + const Elf32_Shdr *header = sentry__module_get_addr( + module, elf->e_shoff + elf->e_shentsize * i, elf->e_shentsize); + ENSURE(header); const char *name = names + header->sh_name; if (header->sh_type == SHT_PROGBITS && strcmp(name, ".text") == 0) { - text = addr + header->sh_offset; + text = sentry__module_get_addr( + module, header->sh_offset, header->sh_size); + ENSURE(text); text_size = header->sh_size; break; } @@ -270,15 +309,18 @@ get_code_id_from_text_fallback(void *base) } return uuid; +fail: + return sentry_uuid_nil(); } bool -sentry__procmaps_read_ids_from_elf(sentry_value_t value, void *elf_ptr) +sentry__procmaps_read_ids_from_elf( + sentry_value_t value, const sentry_module_t *module) { // and try to get the debug id from the elf headers of the loaded // modules size_t code_id_size; - const uint8_t *code_id = get_code_id_from_elf(elf_ptr, &code_id_size); + const uint8_t *code_id = get_code_id_from_elf(module, &code_id_size); sentry_uuid_t uuid = sentry_uuid_nil(); if (code_id) { sentry_value_set_by_key(value, "code_id", @@ -286,13 +328,13 @@ sentry__procmaps_read_ids_from_elf(sentry_value_t value, void *elf_ptr) memcpy(uuid.bytes, code_id, MIN(code_id_size, 16)); } else { - uuid = get_code_id_from_text_fallback(elf_ptr); + uuid = get_code_id_from_text_fallback(module); } // the usage of these is described here: // https://getsentry.github.io/symbolicator/advanced/symbol-server-compatibility/#identifiers - // in particular, the debug_id is a `little-endian GUID`, so we - // have to do appropriate byte-flipping + // in particular, the debug_id is a `little-endian GUID`, so we have to do + // appropriate byte-flipping char *uuid_bytes = uuid.bytes; uint32_t *a = (uint32_t *)uuid_bytes; *a = htonl(*a); @@ -310,62 +352,30 @@ sentry__procmaps_module_to_value(const sentry_module_t *module) { sentry_value_t mod_val = sentry_value_new_object(); sentry_value_set_by_key(mod_val, "type", sentry_value_new_string("elf")); - sentry_value_set_by_key(mod_val, "image_addr", - sentry__value_new_addr((uint64_t)(size_t)module->start)); - sentry_value_set_by_key(mod_val, "image_size", - sentry_value_new_int32( - (int32_t)((size_t)module->end - (size_t)module->start))); sentry_value_set_by_key(mod_val, "code_file", sentry__value_new_string_owned(sentry__slice_to_owned(module->file))); - // At least on the android API-16, x86 simulator, the linker apparently - // does not load the complete file into memory. Or at least, the section - // headers which are located at the end of the file are not loaded, and - // we would be poking into invalid memory. To be safe, we mmap the complete - // file from disk, so we have the on-disk layout, and are independent of how - // the runtime linker would load or re-order any sections. The exception - // here is the linux-gate, which is not an actual file on disk, so we - // actually poke at its memory. - if (sentry__slice_eq(module->file, LINUX_GATE)) { - if (!is_elf_module(module->start)) { - goto fail; - } - sentry__procmaps_read_ids_from_elf(mod_val, module->start); - } else { - char *filename = sentry__slice_to_owned(module->file); - sentry_mmap_t mm; - if (!sentry__mmap_file(&mm, filename)) { - sentry_free(filename); - goto fail; - } - sentry_free(filename); - - if (!is_elf_module(mm.ptr)) { - sentry__mmap_close(&mm); - goto fail; - } - - sentry__procmaps_read_ids_from_elf(mod_val, mm.ptr); + const sentry_mapped_region_t *first_mapping = &module->mappings[0]; + const sentry_mapped_region_t *last_mapping + = &module->mappings[module->num_mappings - 1]; + sentry_value_set_by_key( + mod_val, "image_addr", sentry__value_new_addr(first_mapping->addr)); + sentry_value_set_by_key(mod_val, "image_size", + sentry_value_new_int32( + last_mapping->addr + last_mapping->size - first_mapping->addr)); - sentry__mmap_close(&mm); - } + sentry__procmaps_read_ids_from_elf(mod_val, module); return mod_val; - -fail: - sentry_value_decref(mod_val); - return sentry_value_new_null(); } static void try_append_module(sentry_value_t modules, const sentry_module_t *module) { - if (!module->file.ptr) { + if (!module->file.ptr || !module->num_mappings) { return; } - SENTRY_TRACEF( - "inspecting module \"%.*s\"", (int)module->file.len, module->file.ptr); sentry_value_t mod_val = sentry__procmaps_module_to_value(module); if (!sentry_value_is_null(mod_val)) { sentry_value_append(modules, mod_val); @@ -383,7 +393,7 @@ typedef Elf64_auxv_t elf_aux_entry; #endif // See http://man7.org/linux/man-pages/man7/vdso.7.html -static void * +static uint64_t get_linux_vdso(void) { // this is adapted from: @@ -400,11 +410,21 @@ get_linux_vdso(void) && one_aux_entry.a_type != AT_NULL) { if (one_aux_entry.a_type == AT_SYSINFO_EHDR) { close(fd); - return (void *)one_aux_entry.a_un.a_val; + return (uint64_t)one_aux_entry.a_un.a_val; } } close(fd); - return NULL; + return 0; +} + +static bool +is_valid_elf_header(void *start) +{ + // we try to interpret `addr` as an ELF file, which should start with a + // magic number... + const unsigned char *e_ident = start; + return e_ident[EI_MAG0] == ELFMAG0 && e_ident[EI_MAG1] == ELFMAG1 + && e_ident[EI_MAG2] == ELFMAG2 && e_ident[EI_MAG3] == ELFMAG3; } static void @@ -440,46 +460,61 @@ load_modules(sentry_value_t modules) } char *current_line = contents; - void *linux_vdso = get_linux_vdso(); + uint64_t linux_vdso = get_linux_vdso(); // we have multiple memory maps per file, and we need to merge their offsets // based on the filename. Luckily, the maps are ordered by filename, so yay - sentry_module_t last_module = { (void *)-1, 0, { NULL, 0 } }; + sentry_module_t last_module = { 0 }; while (true) { - sentry_module_t module = { 0, 0, { NULL, 0 } }; + sentry_parsed_module_t module = { 0 }; int read = sentry__procmaps_parse_module_line(current_line, &module); current_line += read; if (!read) { break; } - // for the vdso, we use the special filename `linux-gate.so`, - // otherwise we check that we have a valid pathname (with a `/` inside), - // and skip over things that end in `)`, because entries marked as - // `(deleted)` might crash when dereferencing, trying to check if its - // a valid elf file. - char *slash; - if (module.start && module.start == linux_vdso) { - module.file = LINUX_GATE; - } else if (!module.start || !module.file.len - || module.file.ptr[module.file.len - 1] == ')' - || (slash = strchr(module.file.ptr, '/')) == NULL - || slash > module.file.ptr + module.file.len + // skip mappings that are not readable + if (!module.start || module.permissions[0] != 'r') { + continue; + } + // skip mappings in `/dev/` or mappings that have no filename + if (!module.file.len || (module.file.len >= 5 && memcmp("/dev/", module.file.ptr, 5) == 0)) { continue; } + // for the vdso, we use the special filename `linux-gate.so` + if (module.start && module.start == linux_vdso) { + module.file = LINUX_GATE; + } else if (module.file.ptr[0] != '/') { + // and skip all mappings that are not a file + continue; + } - if (last_module.file.len - && !sentry__slice_eq(last_module.file, module.file)) { - try_append_module(modules, &last_module); - last_module = module; - } else { - // otherwise merge it - last_module.start = MIN(last_module.start, module.start); - last_module.end = MAX(last_module.end, module.end); - last_module.file = module.file; + if (is_valid_elf_header((void *)(size_t)module.start)) { + // On android, we sometimes have multiple mappings for the same + // inode at the same offset, such as this, excuse the auto-format + // here: + // 737b5570d000-737b5570e000 r--p 00000000 07:70 34 + // /apex/com.android.runtime/lib64/bionic/libdl.so + // 737b5570e000-737b5570f000 r-xp 00000000 07:70 34 + // /apex/com.android.runtime/lib64/bionic/libdl.so + // 737b5570f000-737b55710000 r--p 00000000 07:70 34 + // /apex/com.android.runtime/lib64/bionic/libdl.so + + if (!is_duplicated_mapping(&last_module, &module)) { + // try to append the module based on the mappings that we have + // found so far + try_append_module(modules, &last_module); + + // start a new module based on the current mapping + memset(&last_module, 0, sizeof(sentry_module_t)); + + last_module.file = module.file; + } } + + sentry__module_mapping_push(&last_module, &module); } try_append_module(modules, &last_module); sentry_free(contents); diff --git a/src/modulefinder/sentry_modulefinder_linux.h b/src/modulefinder/sentry_modulefinder_linux.h index 8d5adaf71..71654521c 100644 --- a/src/modulefinder/sentry_modulefinder_linux.h +++ b/src/modulefinder/sentry_modulefinder_linux.h @@ -4,26 +4,45 @@ #include "sentry_boot.h" #include "sentry_slice.h" +#define SENTRY_MAX_MAPPINGS 5 + typedef struct { - void *start; - void *end; + uint64_t start; + uint64_t end; + uint64_t offset; + char permissions[5]; + uint64_t inode; sentry_slice_t file; -} sentry_module_t; +} sentry_parsed_module_t; typedef struct { - void *ptr; - size_t len; - int fd; -} sentry_mmap_t; + uint64_t offset; // the offset in the mapped file + uint64_t size; // size of this mapping + uint64_t addr; // addr in memory of the mapping +} sentry_mapped_region_t; -#if SENTRY_UNITTEST -bool sentry__mmap_file(sentry_mmap_t *mapping, const char *path); -void sentry__mmap_close(sentry_mmap_t *mapping); +typedef struct { + sentry_slice_t file; + sentry_mapped_region_t mappings[SENTRY_MAX_MAPPINGS]; + uint64_t offset_in_inode; + uint64_t mappings_inode; + uint8_t num_mappings; +} sentry_module_t; -bool sentry__procmaps_read_ids_from_elf(sentry_value_t value, void *elf_ptr); +#if SENTRY_UNITTEST +bool sentry__procmaps_read_ids_from_elf( + sentry_value_t value, const sentry_module_t *module); int sentry__procmaps_parse_module_line( - const char *line, sentry_module_t *module); + const char *line, sentry_parsed_module_t *module); + +/** + * Checks that `start_offset` + `size` is a valid contiguous mapping in the + * mapped regions, and returns the translated pointer corresponding to + * `start_offset`. + */ +void *sentry__module_get_addr( + const sentry_module_t *module, uint64_t start_offset, uint64_t size); #endif #endif diff --git a/tests/unit/test_modulefinder.c b/tests/unit/test_modulefinder.c index 9ab1d1935..90fc31c06 100644 --- a/tests/unit/test_modulefinder.c +++ b/tests/unit/test_modulefinder.c @@ -37,12 +37,45 @@ SENTRY_TEST(module_finder) sentry_clear_modulecache(); } +SENTRY_TEST(module_addr) +{ +#if !defined(SENTRY_PLATFORM_LINUX) + SKIP_TEST(); +#else + sentry_module_t module = { 0 }; + module.num_mappings = 3; + // | | | | | | | | + // 00000 1111111111 + module.mappings[0].offset = 0; + module.mappings[0].size = 5; + module.mappings[0].addr = 10; + // here is a gap in the address space of size 10 + module.mappings[1].offset = 5; + module.mappings[1].size = 10; + module.mappings[1].addr = 25; + + void *ptr; + + ptr = sentry__module_get_addr(&module, 0, 5); + TEST_CHECK(ptr == (void *)10); + + ptr = sentry__module_get_addr(&module, 0, 6); + TEST_CHECK(ptr == NULL); // not contiguous + + ptr = sentry__module_get_addr(&module, 7, 8); + TEST_CHECK(ptr == (void *)27); + + ptr = sentry__module_get_addr(&module, 7, 9); + TEST_CHECK(ptr == NULL); // too big +#endif +} + SENTRY_TEST(procmaps_parser) { #if !defined(SENTRY_PLATFORM_LINUX) || __SIZEOF_POINTER__ != 8 SKIP_TEST(); #else - sentry_module_t mod; + sentry_parsed_module_t mod; char contents[] = "7fdb549ce000-7fdb54bb5000 r-xp 00000000 08:01 3803938 " " /lib/x86_64-linux-gnu/libc-2.27.so\n" @@ -57,8 +90,8 @@ SENTRY_TEST(procmaps_parser) read = sentry__procmaps_parse_module_line(lines, &mod); lines += read; TEST_CHECK(read); - TEST_CHECK(mod.start == (void *)0x7fdb549ce000); - TEST_CHECK(mod.end == (void *)0x7fdb54bb5000); + TEST_CHECK(mod.start == 0x7fdb549ce000); + TEST_CHECK(mod.end == 0x7fdb54bb5000); TEST_CHECK(strncmp(mod.file.ptr, "/lib/x86_64-linux-gnu/libc-2.27.so", mod.file.len) == 0); @@ -66,21 +99,21 @@ SENTRY_TEST(procmaps_parser) read = sentry__procmaps_parse_module_line(lines, &mod); lines += read; TEST_CHECK(read); - TEST_CHECK(mod.start == (void *)0x7f14753de000); - TEST_CHECK(mod.end == (void *)0x7f14755de000); + TEST_CHECK(mod.start == 0x7f14753de000); + TEST_CHECK(mod.end == 0x7f14755de000); read = sentry__procmaps_parse_module_line(lines, &mod); lines += read; TEST_CHECK(read); - TEST_CHECK(mod.start == (void *)0x7fe714493000); - TEST_CHECK(mod.end == (void *)0x7fe714494000); + TEST_CHECK(mod.start == 0x7fe714493000); + TEST_CHECK(mod.end == 0x7fe714494000); TEST_CHECK(mod.file.ptr == NULL); read = sentry__procmaps_parse_module_line(lines, &mod); lines += read; TEST_CHECK(read); - TEST_CHECK(mod.start == (void *)0x7fff8ca67000); - TEST_CHECK(mod.end == (void *)0x7fff8ca88000); + TEST_CHECK(mod.start == 0x7fff8ca67000); + TEST_CHECK(mod.end == 0x7fff8ca88000); TEST_CHECK(strncmp(mod.file.ptr, "[vdso]", mod.file.len) == 0); read = sentry__procmaps_parse_module_line(lines, &mod); @@ -98,16 +131,19 @@ SENTRY_TEST(buildid_fallback) sentry_path_t *dir = sentry__path_dir(path); sentry__path_free(path); + sentry_module_t module = { 0 }; + module.num_mappings = 1; + size_t *file_size = &module.mappings[0].size; + char **buf = (char **)&module.mappings[0].addr; + sentry_value_t with_id_val = sentry_value_new_object(); - sentry_mmap_t with_id_map; sentry_path_t *with_id_path = sentry__path_join_str(dir, "../fixtures/with-buildid.so"); - TEST_CHECK(sentry__mmap_file(&with_id_map, with_id_path->path)); + *buf = sentry__path_read_to_buffer(with_id_path, file_size); sentry__path_free(with_id_path); - TEST_CHECK( - sentry__procmaps_read_ids_from_elf(with_id_val, with_id_map.ptr)); - sentry__mmap_close(&with_id_map); + TEST_CHECK(sentry__procmaps_read_ids_from_elf(with_id_val, &module)); + sentry_free(*buf); TEST_CHECK_STRING_EQUAL( sentry_value_as_string(sentry_value_get_by_key(with_id_val, "code_id")), @@ -118,15 +154,13 @@ SENTRY_TEST(buildid_fallback) sentry_value_decref(with_id_val); sentry_value_t x86_exe_val = sentry_value_new_object(); - sentry_mmap_t x86_exe_map; sentry_path_t *x86_exe_path = sentry__path_join_str(dir, "../fixtures/sentry_example"); - TEST_CHECK(sentry__mmap_file(&x86_exe_map, x86_exe_path->path)); + *buf = sentry__path_read_to_buffer(x86_exe_path, file_size); sentry__path_free(x86_exe_path); - TEST_CHECK( - sentry__procmaps_read_ids_from_elf(x86_exe_val, x86_exe_map.ptr)); - sentry__mmap_close(&x86_exe_map); + TEST_CHECK(sentry__procmaps_read_ids_from_elf(x86_exe_val, &module)); + sentry_free(*buf); TEST_CHECK_STRING_EQUAL( sentry_value_as_string(sentry_value_get_by_key(x86_exe_val, "code_id")), @@ -137,15 +171,13 @@ SENTRY_TEST(buildid_fallback) sentry_value_decref(x86_exe_val); sentry_value_t without_id_val = sentry_value_new_object(); - sentry_mmap_t without_id_map; sentry_path_t *without_id_path = sentry__path_join_str(dir, "../fixtures/without-buildid.so"); - TEST_CHECK(sentry__mmap_file(&without_id_map, without_id_path->path)); + *buf = sentry__path_read_to_buffer(without_id_path, file_size); sentry__path_free(without_id_path); - TEST_CHECK( - sentry__procmaps_read_ids_from_elf(without_id_val, without_id_map.ptr)); - sentry__mmap_close(&without_id_map); + TEST_CHECK(sentry__procmaps_read_ids_from_elf(without_id_val, &module)); + sentry_free(*buf); TEST_CHECK(sentry_value_is_null( sentry_value_get_by_key(without_id_val, "code_id"))); @@ -155,15 +187,13 @@ SENTRY_TEST(buildid_fallback) sentry_value_decref(without_id_val); sentry_value_t x86_lib_val = sentry_value_new_object(); - sentry_mmap_t x86_lib_map; sentry_path_t *x86_lib_path = sentry__path_join_str(dir, "../fixtures/libstdc++.so"); - TEST_CHECK(sentry__mmap_file(&x86_lib_map, x86_lib_path->path)); + *buf = sentry__path_read_to_buffer(x86_lib_path, file_size); sentry__path_free(x86_lib_path); - TEST_CHECK( - sentry__procmaps_read_ids_from_elf(x86_lib_val, x86_lib_map.ptr)); - sentry__mmap_close(&x86_lib_map); + TEST_CHECK(sentry__procmaps_read_ids_from_elf(x86_lib_val, &module)); + sentry_free(*buf); TEST_CHECK( sentry_value_is_null(sentry_value_get_by_key(x86_lib_val, "code_id"))); diff --git a/tests/unit/tests.inc b/tests/unit/tests.inc index 079a3c4f3..08908530a 100644 --- a/tests/unit/tests.inc +++ b/tests/unit/tests.inc @@ -18,6 +18,7 @@ XX(invalid_dsn) XX(invalid_proxy) XX(iso_time) XX(lazy_attachments) +XX(module_addr) XX(module_finder) XX(mpack_newlines) XX(mpack_removed_tags) diff --git a/tests/valgrind.txt b/tests/valgrind.txt index e69de29bb..c3c0c6c5e 100644 --- a/tests/valgrind.txt +++ b/tests/valgrind.txt @@ -0,0 +1,29 @@ +{ + Checking the ELF Header of readable mapped memory + Memcheck:Addr1 + fun:is_valid_elf_header +} +{ + Reading Debug-Ids from readable mapped memory + Memcheck:Addr1 + ... + fun:sentry__procmaps_read_ids_from_elf +} +{ + Reading Debug-Ids from readable mapped memory + Memcheck:Addr2 + ... + fun:sentry__procmaps_read_ids_from_elf +} +{ + Reading Debug-Ids from readable mapped memory + Memcheck:Addr4 + ... + fun:sentry__procmaps_read_ids_from_elf +} +{ + Reading Debug-Ids from readable mapped memory + Memcheck:Addr8 + ... + fun:sentry__procmaps_read_ids_from_elf +} From 1198758f8287d05fbc133052a2512d3f20808a4d Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Fri, 9 Apr 2021 17:39:14 +0200 Subject: [PATCH 05/49] build: Avoid building all targets (#512) It looks like cmake is broken and builds ALL the targets when the parallel option is specified first, lol --- tests/cmake.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/cmake.py b/tests/cmake.py index f199cf2c4..2f6608dc6 100644 --- a/tests/cmake.py +++ b/tests/cmake.py @@ -159,9 +159,10 @@ def cmake(cwd, targets, options=None): # CodeChecker invocations and options are documented here: # https://github.com/Ericsson/codechecker/blob/master/docs/analyzer/user_guide.md - buildcmd = [*cmake, "--build", ".", "--parallel"] + buildcmd = [*cmake, "--build", "."] for target in targets: buildcmd.extend(["--target", target]) + buildcmd.append("--parallel") if "code-checker" in os.environ.get("RUN_ANALYZER", ""): buildcmd = [ "CodeChecker", From 5fcb1dc4d1c8b85fc5ab225d29c7394b375fd1e9 Mon Sep 17 00:00:00 2001 From: Luke Street Date: Mon, 12 Apr 2021 11:37:56 -0400 Subject: [PATCH 06/49] fix: Update Crashpad to 2021-04-12 and fix macOS universal build (#513) --- .github/workflows/ci.yml | 5 +++++ external/crashpad | 2 +- tests/cmake.py | 2 ++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 357f6c756..c9872eb82 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -62,6 +62,10 @@ jobs: - name: macOS (xcode llvm) os: macOs-latest ERROR_ON_WARNINGS: 1 + - name: macOS (xcode llvm + universal) + os: macOs-latest + ERROR_ON_WARNINGS: 1 + CMAKE_DEFINES: -DCMAKE_OSX_ARCHITECTURES=arm64;x86_64 - name: macOS (clang 11 + asan + llvm-cov) os: macOs-latest CC: clang @@ -93,6 +97,7 @@ jobs: RUN_ANALYZER: ${{ matrix.RUN_ANALYZER }} ANDROID_API: ${{ matrix.ANDROID_API }} ANDROID_NDK: ${{ matrix.ANDROID_NDK }} + CMAKE_DEFINES: ${{ matrix.CMAKE_DEFINES }} steps: - uses: actions/checkout@v2 diff --git a/external/crashpad b/external/crashpad index 2820e9756..cf5ab6450 160000 --- a/external/crashpad +++ b/external/crashpad @@ -1 +1 @@ -Subproject commit 2820e975693bc738ef3e40b5a9ef4244880b8034 +Subproject commit cf5ab6450490946fa3ff90b9dc3533a0bf1853f9 diff --git a/tests/cmake.py b/tests/cmake.py index 2f6608dc6..c81c3cc4f 100644 --- a/tests/cmake.py +++ b/tests/cmake.py @@ -145,6 +145,8 @@ def cmake(cwd, targets, options=None): flags = "-fprofile-instr-generate -fcoverage-mapping" configcmd.append("-DCMAKE_C_FLAGS='{}'".format(flags)) configcmd.append("-DCMAKE_CXX_FLAGS='{}'".format(flags)) + if "CMAKE_DEFINES" in os.environ: + configcmd.extend(os.environ.get("CMAKE_DEFINES").split()) env = dict(os.environ) env["CFLAGS"] = env["CXXFLAGS"] = " ".join(cflags) From 6f54c67172791c159f9be4b02d3af0a308feadbc Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Mon, 19 Apr 2021 11:17:43 +0200 Subject: [PATCH 07/49] feat: Invoke before_send hook when using Crashpad (#519) --- README.md | 2 +- include/sentry.h | 6 +++++- src/backends/sentry_backend_crashpad.cpp | 8 ++++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0e43aed4c..2755f40f5 100644 --- a/README.md +++ b/README.md @@ -295,7 +295,7 @@ Other important configuration options include: ## Known Limitations -- The crashpad backend currently has no support for notifying the crashing +- The crashpad backend on macOS currently has no support for notifying the crashing process, and can thus not properly terminate sessions or call the registered `before_send` hook. It will also lose any events that have been queued for sending at time of crash. diff --git a/include/sentry.h b/include/sentry.h index b7e4978c7..215963354 100644 --- a/include/sentry.h +++ b/include/sentry.h @@ -530,7 +530,7 @@ typedef struct sentry_options_s sentry_options_t; * In case of `false`, sentry will log an error, but continue with freeing the * transport. * * `free_func`: Frees the transports `state`. This hook might be called even - * though `shudown_func` returned `false` previously. + * though `shutdown_func` returned `false` previously. * * The transport interface might be extended in the future with hooks to flush * its internal queue without shutting down, and to dump its internal queue to @@ -635,8 +635,12 @@ SENTRY_API void sentry_options_set_transport( * same event. In case the event should be discarded, the callback needs to * call `sentry_value_decref` on the provided event, and return a * `sentry_value_new_null()` instead. + * * This function may be invoked inside of a signal handler and must be safe for * that purpose, see https://man7.org/linux/man-pages/man7/signal-safety.7.html. + * On Windows, it may be called from inside of a `UnhandledExceptionFilter`, see + * the documentation on SEH (structured exception handling) for more information + * https://docs.microsoft.com/en-us/windows/win32/debug/structured-exception-handling */ typedef sentry_value_t (*sentry_event_function_t)( sentry_value_t event, void *hint, void *closure); diff --git a/src/backends/sentry_backend_crashpad.cpp b/src/backends/sentry_backend_crashpad.cpp index 04f71b6bd..f629ebadb 100644 --- a/src/backends/sentry_backend_crashpad.cpp +++ b/src/backends/sentry_backend_crashpad.cpp @@ -114,6 +114,14 @@ sentry__crashpad_handler(int UNUSED(signum), siginfo_t *UNUSED(info), SENTRY_WITH_OPTIONS (options) { sentry__write_crash_marker(options); + sentry_value_t event = sentry_value_new_event(); + if (options->before_send_func) { + SENTRY_TRACE("invoking `before_send` hook"); + event = options->before_send_func( + event, NULL, options->before_send_data); + } + sentry_value_decref(event); + sentry__record_errors_on_current_session(1); sentry_session_t *session = sentry__end_current_session_with_status( SENTRY_SESSION_STATUS_CRASHED); From 60afb29c8b76c3ef369839b1e52b56329982645c Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Mon, 19 Apr 2021 13:17:07 +0200 Subject: [PATCH 08/49] feat: Add more Event Payload convenience methods (#517) Adds: * `sentry_value_new_exception` * `sentry_value_new_thread` * `sentry_value_new_stacktrace` * `sentry_event_add_exception` * `sentry_event_add_thread` Deprecates `sentry_event_value_add_stacktrace` --- examples/example.c | 21 +++---- include/sentry.h | 87 +++++++++++++++++++++++--- src/backends/sentry_backend_inproc.c | 29 ++------- src/sentry_value.c | 92 +++++++++++++++++++++++++--- tests/assertions.py | 3 +- tests/test_integration_http.py | 3 +- 6 files changed, 178 insertions(+), 57 deletions(-) diff --git a/examples/example.c b/examples/example.c index 392675a58..7a2742091 100644 --- a/examples/example.c +++ b/examples/example.c @@ -166,21 +166,14 @@ main(int argc, char **argv) sentry_capture_event(event); } if (has_arg(argc, argv, "capture-exception")) { - // TODO: Create a convenience API to create a new exception object, - // and to attach a stacktrace to the exception. - // See also https://github.com/getsentry/sentry-native/issues/235 + sentry_value_t exc = sentry_value_new_exception( + "ParseIntError", "invalid digit found in string"); + if (has_arg(argc, argv, "add-stacktrace")) { + sentry_value_t stacktrace = sentry_value_new_stacktrace(NULL, 0); + sentry_value_set_by_key(exc, "stacktrace", stacktrace); + } sentry_value_t event = sentry_value_new_event(); - sentry_value_t exception = sentry_value_new_object(); - // for example: - sentry_value_set_by_key( - exception, "type", sentry_value_new_string("ParseIntError")); - sentry_value_set_by_key(exception, "value", - sentry_value_new_string("invalid digit found in string")); - sentry_value_t exceptions = sentry_value_new_list(); - sentry_value_append(exceptions, exception); - sentry_value_t values = sentry_value_new_object(); - sentry_value_set_by_key(values, "values", exceptions); - sentry_value_set_by_key(event, "exception", values); + sentry_event_add_exception(event, exc); sentry_capture_event(event); } diff --git a/include/sentry.h b/include/sentry.h index 215963354..2abf08162 100644 --- a/include/sentry.h +++ b/include/sentry.h @@ -338,12 +338,18 @@ typedef enum sentry_level_e { } sentry_level_t; /** - * Creates a new empty event value. + * Creates a new empty Event value. + * + * See https://docs.sentry.io/platforms/native/enriching-events/ for how to + * further work with events, and https://develop.sentry.dev/sdk/event-payloads/ + * for a detailed overview of the possible properties of an Event. */ SENTRY_API sentry_value_t sentry_value_new_event(void); /** - * Creates a new message event value. + * Creates a new Message Event value. + * + * See https://develop.sentry.dev/sdk/event-payloads/message/ * * `logger` can be NULL to omit the logger value. */ @@ -351,13 +357,73 @@ SENTRY_API sentry_value_t sentry_value_new_message_event( sentry_level_t level, const char *logger, const char *text); /** - * Creates a new breadcrumb with a specific type and message. + * Creates a new Breadcrumb with a specific type and message. + * + * See https://develop.sentry.dev/sdk/event-payloads/breadcrumbs/ * * Either parameter can be NULL in which case no such attributes is created. */ SENTRY_API sentry_value_t sentry_value_new_breadcrumb( const char *type, const char *message); +/** + * Creates a new Exception value. + * + * This is intended for capturing language-level exception, such as from a + * try-catch block. `type` and `value` here refer to the exception class and + * a possible description. + * + * See https://develop.sentry.dev/sdk/event-payloads/exception/ + * + * The returned value needs to be attached to an event via + * `sentry_event_add_exception`. + */ +SENTRY_EXPERIMENTAL_API sentry_value_t sentry_value_new_exception( + const char *type, const char *value); + +/** + * Creates a new Thread value. + * + * See https://develop.sentry.dev/sdk/event-payloads/threads/ + * + * The returned value needs to be attached to an event via + * `sentry_event_add_thread`. + * + * `name` can be NULL. + */ +SENTRY_EXPERIMENTAL_API sentry_value_t sentry_value_new_thread( + uint64_t id, const char *name); + +/** + * Creates a new Stack Trace conforming to the Stack Trace Interface. + * + * See https://develop.sentry.dev/sdk/event-payloads/stacktrace/ + * + * The returned object needs to be attached to either an exception + * event, or a thread object. + * + * If `ips` is NULL the current stack trace is captured, otherwise `len` + * stack trace instruction pointers are attached to the event. + */ +SENTRY_EXPERIMENTAL_API sentry_value_t sentry_value_new_stacktrace( + void **ips, size_t len); + +/** + * Adds an Exception to an Event value. + * + * This takes ownership of the `exception`. + */ +SENTRY_EXPERIMENTAL_API void sentry_event_add_exception( + sentry_value_t event, sentry_value_t exception); + +/** + * Adds a Thread to an Event value. + * + * This takes ownership of the `thread`. + */ +SENTRY_EXPERIMENTAL_API void sentry_event_add_thread( + sentry_value_t event, sentry_value_t thread); + /* -- Experimental APIs -- */ /** @@ -371,10 +437,15 @@ SENTRY_EXPERIMENTAL_API char *sentry_value_to_msgpack( sentry_value_t value, size_t *size_out); /** - * Adds a stacktrace to an event. + * Adds a stack trace to an event. + * + * The stack trace is added as part of a new thread object. + * This function is **deprecated** in favor of using + * `sentry_value_new_stacktrace` in combination with `sentry_value_new_thread` + * and `sentry_event_add_thread`. * - * If `ips` is NULL the current stacktrace is captured, otherwise `len` - * stacktrace instruction pointers are attached to the event. + * If `ips` is NULL the current stack trace is captured, otherwise `len` + * stack trace instruction pointers are attached to the event. */ SENTRY_EXPERIMENTAL_API void sentry_event_value_add_stacktrace( sentry_value_t event, void **ips, size_t len); @@ -398,7 +469,7 @@ typedef struct sentry_ucontext_s { * * If the address is given in `addr` the stack is unwound form there. * Otherwise (NULL is passed) the current instruction pointer is used as - * start address. The stacktrace is written to `stacktrace_out` with upt o + * start address. The stack trace is written to `stacktrace_out` with up to * `max_len` frames being written. The actual number of unwound stackframes * is returned. */ @@ -408,7 +479,7 @@ SENTRY_EXPERIMENTAL_API size_t sentry_unwind_stack( /** * Unwinds the stack from the given context. * - * The stacktrace is written to `stacktrace_out` with upt o `max_len` frames + * The stack trace is written to `stacktrace_out` with up to `max_len` frames * being written. The actual number of unwound stackframes is returned. */ SENTRY_EXPERIMENTAL_API size_t sentry_unwind_stack_from_ucontext( diff --git a/src/backends/sentry_backend_inproc.c b/src/backends/sentry_backend_inproc.c index a0b84fdd9..d3b77fd40 100644 --- a/src/backends/sentry_backend_inproc.c +++ b/src/backends/sentry_backend_inproc.c @@ -177,13 +177,9 @@ make_signal_event( sentry_value_set_by_key( event, "level", sentry__value_new_level(SENTRY_LEVEL_FATAL)); - sentry_value_t exc = sentry_value_new_object(); - sentry_value_set_by_key(exc, "type", - sentry_value_new_string( - sig_slot ? sig_slot->signame : "UNKNOWN_SIGNAL")); - sentry_value_set_by_key(exc, "value", - sentry_value_new_string( - sig_slot ? sig_slot->sigdesc : "UnknownSignal")); + sentry_value_t exc = sentry_value_new_exception( + sig_slot ? sig_slot->signame : "UNKNOWN_SIGNAL", + sig_slot ? sig_slot->sigdesc : "UnknownSignal"); sentry_value_t mechanism = sentry_value_new_object(); sentry_value_set_by_key(exc, "mechanism", mechanism); @@ -217,25 +213,12 @@ make_signal_event( } SENTRY_TRACEF("captured backtrace with %lu frames", frame_count); - sentry_value_t frames = sentry__value_new_list_with_size(frame_count); - for (size_t i = 0; i < frame_count; i++) { - sentry_value_t frame = sentry_value_new_object(); - sentry_value_set_by_key(frame, "instruction_addr", - sentry__value_new_addr( - (uint64_t)(size_t)backtrace[frame_count - i - 1])); - sentry_value_append(frames, frame); - } - - sentry_value_t stacktrace = sentry_value_new_object(); - sentry_value_set_by_key(stacktrace, "frames", frames); + sentry_value_t stacktrace + = sentry_value_new_stacktrace(&backtrace[0], frame_count); sentry_value_set_by_key(exc, "stacktrace", stacktrace); - sentry_value_t exceptions = sentry_value_new_object(); - sentry_value_t values = sentry_value_new_list(); - sentry_value_set_by_key(exceptions, "values", values); - sentry_value_append(values, exc); - sentry_value_set_by_key(event, "exception", exceptions); + sentry_event_add_exception(event, exc); return event; } diff --git a/src/sentry_value.c b/src/sentry_value.c index 1e4b72525..7a14a980f 100644 --- a/src/sentry_value.c +++ b/src/sentry_value.c @@ -997,6 +997,8 @@ sentry_value_new_event(void) sentry__value_new_string_owned( sentry__msec_time_to_iso8601(sentry__msec_time()))); + sentry_value_set_by_key(rv, "platform", sentry_value_new_string("native")); + return rv; } @@ -1036,8 +1038,38 @@ sentry_value_new_breadcrumb(const char *type, const char *message) return rv; } -void -sentry_event_value_add_stacktrace(sentry_value_t event, void **ips, size_t len) +sentry_value_t +sentry_value_new_exception(const char *type, const char *value) +{ + sentry_value_t exc = sentry_value_new_object(); + sentry_value_set_by_key(exc, "type", sentry_value_new_string(type)); + sentry_value_set_by_key(exc, "value", sentry_value_new_string(value)); + return exc; +} + +sentry_value_t +sentry_value_new_thread(uint64_t id, const char *name) +{ + sentry_value_t thread = sentry_value_new_object(); + + // NOTE: values end up as JSON, which has no support for `u64`. + char buf[20 + 1]; + size_t written + = (size_t)snprintf(buf, sizeof(buf), "%llu", (unsigned long long)id); + if (written < sizeof(buf)) { + buf[written] = '\0'; + sentry_value_set_by_key(thread, "id", sentry_value_new_string(buf)); + } + + if (name) { + sentry_value_set_by_key(thread, "name", sentry_value_new_string(name)); + } + + return thread; +} + +sentry_value_t +sentry_value_new_stacktrace(void **ips, size_t len) { void *walked_backtrace[256]; @@ -1058,14 +1090,56 @@ sentry_event_value_add_stacktrace(sentry_value_t event, void **ips, size_t len) sentry_value_t stacktrace = sentry_value_new_object(); sentry_value_set_by_key(stacktrace, "frames", frames); - sentry_value_t thread = sentry_value_new_object(); - sentry_value_set_by_key(thread, "stacktrace", stacktrace); + return stacktrace; +} + +static sentry_value_t +sentry__get_or_insert_values_list(sentry_value_t parent, const char *key) +{ + sentry_value_t obj = sentry_value_get_by_key(parent, key); + if (sentry_value_is_null(obj)) { + obj = sentry_value_new_object(); + sentry_value_set_by_key(parent, key, obj); + } + + sentry_value_type_t type = sentry_value_get_type(obj); + sentry_value_t values = sentry_value_new_null(); + if (type == SENTRY_VALUE_TYPE_OBJECT) { + values = sentry_value_get_by_key(obj, "values"); + if (sentry_value_is_null(values)) { + values = sentry_value_new_list(); + sentry_value_set_by_key(obj, "values", values); + } + } else if (type == SENTRY_VALUE_TYPE_LIST) { + values = obj; + } - sentry_value_t values = sentry_value_new_list(); - sentry_value_append(values, thread); + return values; +} + +void +sentry_event_add_exception(sentry_value_t event, sentry_value_t exception) +{ + sentry_value_t exceptions + = sentry__get_or_insert_values_list(event, "exception"); + sentry_value_append(exceptions, exception); +} - sentry_value_t threads = sentry_value_new_object(); - sentry_value_set_by_key(threads, "values", values); +void +sentry_event_add_thread(sentry_value_t event, sentry_value_t thread) +{ + sentry_value_t threads + = sentry__get_or_insert_values_list(event, "threads"); + sentry_value_append(threads, thread); +} + +void +sentry_event_value_add_stacktrace(sentry_value_t event, void **ips, size_t len) +{ + sentry_value_t stacktrace = sentry_value_new_stacktrace(ips, len); + + sentry_value_t thread = sentry_value_new_object(); + sentry_value_set_by_key(thread, "stacktrace", stacktrace); - sentry_value_set_by_key(event, "threads", threads); + sentry_event_add_thread(event, thread); } diff --git a/tests/assertions.py b/tests/assertions.py index c5b4563cc..6995aff54 100644 --- a/tests/assertions.py +++ b/tests/assertions.py @@ -163,8 +163,7 @@ def assert_exception(envelope): "type": "ParseIntError", "value": "invalid digit found in string", } - expected = {"exception": {"values": [exception]}} - assert matches(event, expected) + assert matches(event["exception"]["values"][0], exception) assert_timestamp(event["timestamp"]) diff --git a/tests/test_integration_http.py b/tests/test_integration_http.py index c02d03312..e32b2e922 100644 --- a/tests/test_integration_http.py +++ b/tests/test_integration_http.py @@ -119,7 +119,7 @@ def test_exception_and_session_http(cmake, httpserver): run( tmp_path, "sentry_example", - ["log", "start-session", "capture-exception"], + ["log", "start-session", "capture-exception", "add-stacktrace"], check=True, env=env, ) @@ -129,6 +129,7 @@ def test_exception_and_session_http(cmake, httpserver): envelope = Envelope.deserialize(output) assert_exception(envelope) + assert_stacktrace(envelope, inside_exception=True) assert_session(envelope, {"init": True, "status": "ok", "errors": 1}) output = httpserver.log[1][0].get_data() From 3c7f5b2cb43929f6b17f1046c14a6ad62fdc5a58 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Mon, 19 Apr 2021 13:23:38 +0200 Subject: [PATCH 09/49] feat: Introduce `sentry_close` (#518) This replaces the former `sentry_shutdown`, which is being forwarded. --- README.md | 2 +- examples/example.c | 2 +- include/sentry.h | 11 ++++++++++- src/sentry_core.c | 10 ++++++++-- tests/unit/test_attachments.c | 2 +- tests/unit/test_basic.c | 4 ++-- tests/unit/test_consent.c | 10 +++++----- tests/unit/test_envelopes.c | 2 +- tests/unit/test_logger.c | 4 ++-- tests/unit/test_session.c | 4 ++-- tests/unit/test_uninit.c | 8 ++++---- 11 files changed, 37 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 2755f40f5..d26247b72 100644 --- a/README.md +++ b/README.md @@ -283,7 +283,7 @@ sentry_init(options); // your application code … -sentry_shutdown(); +sentry_close(); ``` Other important configuration options include: diff --git a/examples/example.c b/examples/example.c index 7a2742091..5b75f749c 100644 --- a/examples/example.c +++ b/examples/example.c @@ -179,7 +179,7 @@ main(int argc, char **argv) } // make sure everything flushes - sentry_shutdown(); + sentry_close(); if (has_arg(argc, argv, "sleep-after-shutdown")) { sleep_s(1); } diff --git a/include/sentry.h b/include/sentry.h index 2abf08162..491a39f9f 100644 --- a/include/sentry.h +++ b/include/sentry.h @@ -864,7 +864,7 @@ SENTRY_API void sentry_options_set_logger( * Automatic session tracking is enabled by default and is equivalent to calling * `sentry_start_session` after startup. * There can only be one running session, and the current session will always be - * closed implicitly by `sentry_shutdown`, when starting a new session with + * closed implicitly by `sentry_close`, when starting a new session with * `sentry_start_session`, or manually by calling `sentry_end_session`. */ SENTRY_API void sentry_options_set_auto_session_tracking( @@ -1017,6 +1017,15 @@ SENTRY_API int sentry_init(sentry_options_t *options); * * Returns 0 on success. */ +SENTRY_API int sentry_close(void); + +/** + * Shuts down the sentry client and forces transports to flush out. + * + * This is a **deprecated** alias for `sentry_close`. + * + * Returns 0 on success. + */ SENTRY_API int sentry_shutdown(void); /** diff --git a/src/sentry_core.c b/src/sentry_core.c index 9e7aab29c..43cf9ed74 100644 --- a/src/sentry_core.c +++ b/src/sentry_core.c @@ -72,7 +72,7 @@ sentry__should_skip_upload(void) int sentry_init(sentry_options_t *options) { - sentry_shutdown(); + sentry_close(); sentry_logger_t logger = { NULL, NULL }; if (options->debug) { @@ -179,7 +179,7 @@ sentry_init(sentry_options_t *options) } int -sentry_shutdown(void) +sentry_close(void) { sentry_end_session(); @@ -219,6 +219,12 @@ sentry_shutdown(void) return (int)dumped_envelopes; } +int +sentry_shutdown(void) +{ + return sentry_close(); +} + int sentry_reinstall_backend(void) { diff --git a/tests/unit/test_attachments.c b/tests/unit/test_attachments.c index 6a84413b3..de22327a9 100644 --- a/tests/unit/test_attachments.c +++ b/tests/unit/test_attachments.c @@ -82,7 +82,7 @@ SENTRY_TEST(lazy_attachments) != NULL); sentry_free(serialized); - sentry_shutdown(); + sentry_close(); sentry__path_remove(existing); sentry__path_remove(non_existing); diff --git a/tests/unit/test_basic.c b/tests/unit/test_basic.c index 1566f31ee..0f68b2165 100644 --- a/tests/unit/test_basic.c +++ b/tests/unit/test_basic.c @@ -57,7 +57,7 @@ SENTRY_TEST(basic_function_transport) sentry_capture_event(sentry_value_new_message_event(SENTRY_LEVEL_INFO, "root", "not captured either due to revoked consent")); - sentry_shutdown(); + sentry_close(); TEST_CHECK_INT_EQUAL(called, 2); } @@ -90,7 +90,7 @@ SENTRY_TEST(sampling_before_send) sentry_value_new_message_event(SENTRY_LEVEL_INFO, NULL, "foo")); } - sentry_shutdown(); + sentry_close(); TEST_CHECK_INT_EQUAL(called_transport, 0); // well, its random after all diff --git a/tests/unit/test_consent.c b/tests/unit/test_consent.c index 5f04779c8..2d1845fb2 100644 --- a/tests/unit/test_consent.c +++ b/tests/unit/test_consent.c @@ -25,19 +25,19 @@ SENTRY_TEST(basic_consent_tracking) init_consenting_sentry(); TEST_CHECK_INT_EQUAL( sentry_user_consent_get(), SENTRY_USER_CONSENT_UNKNOWN); - sentry_shutdown(); + sentry_close(); init_consenting_sentry(); sentry_user_consent_give(); TEST_CHECK_INT_EQUAL(sentry_user_consent_get(), SENTRY_USER_CONSENT_GIVEN); - sentry_shutdown(); + sentry_close(); init_consenting_sentry(); TEST_CHECK_INT_EQUAL(sentry_user_consent_get(), SENTRY_USER_CONSENT_GIVEN); sentry_user_consent_revoke(); TEST_CHECK_INT_EQUAL( sentry_user_consent_get(), SENTRY_USER_CONSENT_REVOKED); - sentry_shutdown(); + sentry_close(); init_consenting_sentry(); TEST_CHECK_INT_EQUAL( sentry_user_consent_get(), SENTRY_USER_CONSENT_REVOKED); @@ -45,11 +45,11 @@ SENTRY_TEST(basic_consent_tracking) sentry_user_consent_reset(); TEST_CHECK_INT_EQUAL( sentry_user_consent_get(), SENTRY_USER_CONSENT_UNKNOWN); - sentry_shutdown(); + sentry_close(); init_consenting_sentry(); TEST_CHECK_INT_EQUAL( sentry_user_consent_get(), SENTRY_USER_CONSENT_UNKNOWN); - sentry_shutdown(); + sentry_close(); sentry__path_remove_all(path); sentry__path_free(path); diff --git a/tests/unit/test_envelopes.c b/tests/unit/test_envelopes.c index 8c839b593..a579afa3e 100644 --- a/tests/unit/test_envelopes.c +++ b/tests/unit/test_envelopes.c @@ -134,5 +134,5 @@ SENTRY_TEST(serialize_envelope) sentry_envelope_free(envelope); sentry_free(str); - sentry_shutdown(); + sentry_close(); } diff --git a/tests/unit/test_logger.c b/tests/unit/test_logger.c index a4798d319..18948f2bf 100644 --- a/tests/unit/test_logger.c +++ b/tests/unit/test_logger.c @@ -39,12 +39,12 @@ SENTRY_TEST(custom_logger) SENTRY_WARNF("Oh this is %s", "bad"); data.assert_now = false; - sentry_shutdown(); + sentry_close(); TEST_CHECK_INT_EQUAL(data.called, 1); // *really* clear the logger instance options = sentry_options_new(); sentry_init(options); - sentry_shutdown(); + sentry_close(); } diff --git a/tests/unit/test_session.c b/tests/unit/test_session.c index 9f92db368..e79cc266b 100644 --- a/tests/unit/test_session.c +++ b/tests/unit/test_session.c @@ -78,7 +78,7 @@ SENTRY_TEST(session_basics) user, "username", sentry_value_new_string("swatinem")); sentry_set_user(user); - sentry_shutdown(); + sentry_close(); TEST_CHECK_INT_EQUAL(called, 2); } @@ -138,7 +138,7 @@ SENTRY_TEST(count_sampled_events) } assertion.assert_session = true; - sentry_shutdown(); + sentry_close(); TEST_CHECK_INT_EQUAL(assertion.called, 1); } diff --git a/tests/unit/test_uninit.c b/tests/unit/test_uninit.c index 1e08c5118..f9a294c2b 100644 --- a/tests/unit/test_uninit.c +++ b/tests/unit/test_uninit.c @@ -29,7 +29,7 @@ SENTRY_TEST(uninitialized) sentry_set_level(SENTRY_LEVEL_DEBUG); sentry_start_session(); sentry_end_session(); - sentry_shutdown(); + sentry_close(); } SENTRY_TEST(empty_transport) @@ -44,7 +44,7 @@ SENTRY_TEST(empty_transport) sentry_uuid_t id = sentry_capture_event(event); TEST_CHECK(!sentry_uuid_is_nil(&id)); - sentry_shutdown(); + sentry_close(); } SENTRY_TEST(invalid_dsn) @@ -59,7 +59,7 @@ SENTRY_TEST(invalid_dsn) sentry_uuid_t id = sentry_capture_event(event); TEST_CHECK(!sentry_uuid_is_nil(&id)); - sentry_shutdown(); + sentry_close(); } SENTRY_TEST(invalid_proxy) @@ -74,5 +74,5 @@ SENTRY_TEST(invalid_proxy) sentry_uuid_t id = sentry_capture_event(event); TEST_CHECK(!sentry_uuid_is_nil(&id)); - sentry_shutdown(); + sentry_close(); } From 7d6f29f9d5a6f903fa71f25a3bdc48ac72c40f0d Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Tue, 20 Apr 2021 13:04:48 +0200 Subject: [PATCH 10/49] meta: Prepare Changelog for upcoming release (#522) --- CHANGELOG.md | 35 +++++++++++++++++++++++++++++++++++ external/breakpad | 2 +- external/crashpad | 2 +- 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f0b50a7e4..5f5775e5c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,40 @@ # Changelog +## Unreleased + +**Features**: + +- Rewrote the Linux modulefinder which should now work correctly when encountering gaps in the memory mapping of loaded libraries, and supports libraries loaded from a file offset, such as libraries loaded directly from `.apk` files on Android. +- Invoke the `before_send` hook at time of a hard crash when using the Windows or Linux Crashpad backend. +- Added the following new convenience functions: + - `sentry_value_new_exception` + - `sentry_value_new_thread` + - `sentry_value_new_stacktrace` + - `sentry_event_add_exception` + - `sentry_event_add_thread` + - The `sentry_event_value_add_stacktrace` is deprecated. +- Renamed `sentry_shutdown` to `sentry_close`, though the old function is still available. +- Updated Qt integration to Qt 6. + +**Fixes**: + +- Optimized and fixed bugs in the JSON parser/serializer. +- Build fixes for PPC and universal macOS. +- Fixes to build using musl libc. +- Correctness fixes around printf and strftime usage. + +**Internal**: + +- Update Crashpad and Breakpad submodules to 2021-04-12 + +**Thank you**: + +Features, fixes and improvements in this release have been contributed by: + +- [@mastertheknife](https://github.com/mastertheknife) +- [@torarnv](https://github.com/torarnv) +- [@encounter](https://github.com/encounter) + ## 0.4.8 **Features**: diff --git a/external/breakpad b/external/breakpad index 77bf06f4d..bbc999925 160000 --- a/external/breakpad +++ b/external/breakpad @@ -1 +1 @@ -Subproject commit 77bf06f4dee17f3b9021875a0647752c62452aaf +Subproject commit bbc999925cadc1b40f153f1a695062a88be5891a diff --git a/external/crashpad b/external/crashpad index cf5ab6450..fdbe4d7b1 160000 --- a/external/crashpad +++ b/external/crashpad @@ -1 +1 @@ -Subproject commit cf5ab6450490946fa3ff90b9dc3533a0bf1853f9 +Subproject commit fdbe4d7b1053939f2c77193d8e854de5a3c93e17 From c04138612bc43628643c232055c5314b2d87df52 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Tue, 20 Apr 2021 14:06:51 +0200 Subject: [PATCH 11/49] ref: Pass options to scope apply directly (#521) --- src/backends/sentry_backend_crashpad.cpp | 5 +++-- src/sentry_backend.h | 26 +++++++++++------------- src/sentry_core.c | 2 +- src/sentry_core.h | 5 ----- src/sentry_scope.c | 15 +++++++------- src/sentry_scope.h | 3 ++- tests/unit/test_mpack.c | 4 +++- 7 files changed, 28 insertions(+), 32 deletions(-) diff --git a/src/backends/sentry_backend_crashpad.cpp b/src/backends/sentry_backend_crashpad.cpp index f629ebadb..06e028934 100644 --- a/src/backends/sentry_backend_crashpad.cpp +++ b/src/backends/sentry_backend_crashpad.cpp @@ -65,7 +65,8 @@ sentry__crashpad_backend_user_consent_changed(sentry_backend_t *backend) } static void -sentry__crashpad_backend_flush_scope(sentry_backend_t *backend) +sentry__crashpad_backend_flush_scope( + sentry_backend_t *backend, const sentry_options_t *options) { const crashpad_state_t *data = (crashpad_state_t *)backend->data; if (!data->event_path) { @@ -78,7 +79,7 @@ sentry__crashpad_backend_flush_scope(sentry_backend_t *backend) sentry_value_t event = sentry_value_new_object(); SENTRY_WITH_SCOPE (scope) { // we want the scope without any modules or breadcrumbs - sentry__scope_apply_to_event(scope, event, SENTRY_SCOPE_NONE); + sentry__scope_apply_to_event(scope, options, event, SENTRY_SCOPE_NONE); } size_t mpack_size; diff --git a/src/sentry_backend.h b/src/sentry_backend.h index 1eb025d38..02faf3303 100644 --- a/src/sentry_backend.h +++ b/src/sentry_backend.h @@ -11,24 +11,22 @@ * can ensure that any captured crash contains the sentry scope and other * information. */ -struct sentry_backend_s; -typedef struct sentry_backend_s { - int (*startup_func)( - struct sentry_backend_s *, const sentry_options_t *options); - void (*shutdown_func)(struct sentry_backend_s *); - void (*free_func)(struct sentry_backend_s *); - void (*except_func)( - struct sentry_backend_s *, const struct sentry_ucontext_s *); - void (*flush_scope_func)(struct sentry_backend_s *); +typedef struct sentry_backend_s sentry_backend_t; +struct sentry_backend_s { + int (*startup_func)(sentry_backend_t *, const sentry_options_t *options); + void (*shutdown_func)(sentry_backend_t *); + void (*free_func)(sentry_backend_t *); + void (*except_func)(sentry_backend_t *, const struct sentry_ucontext_s *); + void (*flush_scope_func)( + sentry_backend_t *, const sentry_options_t *options); // NOTE: The breadcrumb is not moved into the hook and does not need to be // `decref`-d internally. - void (*add_breadcrumb_func)( - struct sentry_backend_s *, sentry_value_t breadcrumb); - void (*user_consent_changed_func)(struct sentry_backend_s *); - uint64_t (*get_last_crash_func)(struct sentry_backend_s *); + void (*add_breadcrumb_func)(sentry_backend_t *, sentry_value_t breadcrumb); + void (*user_consent_changed_func)(sentry_backend_t *); + uint64_t (*get_last_crash_func)(sentry_backend_t *); void *data; bool can_capture_after_shutdown; -} sentry_backend_t; +}; /** * This will free a previously allocated backend. diff --git a/src/sentry_core.c b/src/sentry_core.c index 43cf9ed74..704d8549c 100644 --- a/src/sentry_core.c +++ b/src/sentry_core.c @@ -376,7 +376,7 @@ sentry__prepare_event(const sentry_options_t *options, sentry_value_t event, if (!options->symbolize_stacktraces) { mode &= ~SENTRY_SCOPE_STACKTRACES; } - sentry__scope_apply_to_event(scope, event, mode); + sentry__scope_apply_to_event(scope, options, event, mode); } if (options->before_send_func) { diff --git a/src/sentry_core.h b/src/sentry_core.h index 156801ff1..46e546630 100644 --- a/src/sentry_core.h +++ b/src/sentry_core.h @@ -70,11 +70,6 @@ sentry_value_t sentry__ensure_event_id( */ const sentry_options_t *sentry__options_getref(void); -/** - * Release the lock on the global options. - */ -void sentry__options_unlock(void); - #define SENTRY_WITH_OPTIONS(Options) \ for (const sentry_options_t *Options = sentry__options_getref(); Options; \ sentry_options_free((sentry_options_t *)Options), Options = NULL) diff --git a/src/sentry_scope.c b/src/sentry_scope.c index 814b6f815..143049e77 100644 --- a/src/sentry_scope.c +++ b/src/sentry_scope.c @@ -127,7 +127,7 @@ sentry__scope_flush_unlock(const sentry_scope_t *scope) // we try to unlock the scope/session lock as soon as possible. The // backend will do its own `WITH_SCOPE` internally. if (options->backend && options->backend->flush_scope_func) { - options->backend->flush_scope_func(options->backend); + options->backend->flush_scope_func(options->backend, options); } } if (!did_unlock) { @@ -237,8 +237,9 @@ sentry__symbolize_stacktrace(sentry_value_t stacktrace) } void -sentry__scope_apply_to_event( - const sentry_scope_t *scope, sentry_value_t event, sentry_scope_mode_t mode) +sentry__scope_apply_to_event(const sentry_scope_t *scope, + const sentry_options_t *options, sentry_value_t event, + sentry_scope_mode_t mode) { #define IS_NULL(Key) sentry_value_is_null(sentry_value_get_by_key(event, Key)) #define SET(Key, Value) sentry_value_set_by_key(event, Key, Value) @@ -264,11 +265,9 @@ sentry__scope_apply_to_event( PLACE_STRING("platform", "native"); - SENTRY_WITH_OPTIONS (options) { - PLACE_STRING("release", options->release); - PLACE_STRING("dist", options->dist); - PLACE_STRING("environment", options->environment); - } + PLACE_STRING("release", options->release); + PLACE_STRING("dist", options->dist); + PLACE_STRING("environment", options->environment); if (IS_NULL("level")) { SET("level", sentry__value_new_level(scope->level)); diff --git a/src/sentry_scope.h b/src/sentry_scope.h index e22cc945d..a6bb6155e 100644 --- a/src/sentry_scope.h +++ b/src/sentry_scope.h @@ -67,7 +67,8 @@ void sentry__scope_flush_unlock(const sentry_scope_t *scope); * attached. */ void sentry__scope_apply_to_event(const sentry_scope_t *scope, - sentry_value_t event, sentry_scope_mode_t mode); + const sentry_options_t *options, sentry_value_t event, + sentry_scope_mode_t mode); /** * This will update a sessions `distinct_id`, which is generated out of other diff --git a/tests/unit/test_mpack.c b/tests/unit/test_mpack.c index d805108eb..391a3d892 100644 --- a/tests/unit/test_mpack.c +++ b/tests/unit/test_mpack.c @@ -17,13 +17,15 @@ SENTRY_TEST(mpack_removed_tags) sentry_set_extra("int", sentry_value_new_int32(1234)); sentry_set_extra("double", sentry_value_new_double(12.34)); + sentry_options_t *options = sentry_options_new(); SENTRY_WITH_SCOPE (scope) { - sentry__scope_apply_to_event(scope, obj, SENTRY_SCOPE_NONE); + sentry__scope_apply_to_event(scope, options, obj, SENTRY_SCOPE_NONE); } size_t size; char *buf = sentry_value_to_msgpack(obj, &size); + sentry_options_free(options); sentry_value_decref(obj); sentry_free(buf); sentry__scope_cleanup(); From 1d62fc599bf448533de87501da5504484ff26d97 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Tue, 20 Apr 2021 15:26:25 +0200 Subject: [PATCH 12/49] fix: Further clean up platform libraries for static linking (#523) --- CMakeLists.txt | 9 +++++---- README.md | 3 ++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fe88796f9..8a74a93ac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -256,8 +256,6 @@ if(SENTRY_TRANSPORT_CURL) string(REPLACE ";" "$" GENEX_CURL_COMPILE_DEFINITIONS "${CURL_COMPILE_DEFINITIONS}") target_link_libraries(sentry PRIVATE $) target_compile_definitions(sentry PRIVATE $) -elseif(SENTRY_TRANSPORT_WINHTTP) - target_link_libraries(sentry PRIVATE winhttp) endif() set_property(TARGET sentry PROPERTY C_VISIBILITY_PRESET hidden) @@ -336,7 +334,6 @@ if(WIN32) target_compile_definitions(sentry PRIVATE "_WIN32_WINNT=0x0501") endif() - target_link_libraries(sentry PRIVATE shlwapi) # crashpad does not support Windows XP toolset if(MSVC AND CMAKE_GENERATOR_TOOLSET MATCHES "_xp$" AND SENTRY_BACKEND_CRASHPAD) message(FATAL_ERROR "MSVC XP toolset does not support Crashpad") @@ -349,7 +346,11 @@ if(ANDROID) elseif(LINUX) set(_SENTRY_PLATFORM_LIBS "dl" "rt") elseif(WIN32) - set(_SENTRY_PLATFORM_LIBS "dbghelp" "version") + set(_SENTRY_PLATFORM_LIBS "dbghelp" "shlwapi" "version") +endif() + +if(SENTRY_TRANSPORT_WINHTTP) + list(APPEND _SENTRY_PLATFORM_LIBS "winhttp") endif() # handle platform threads library diff --git a/README.md b/README.md index d26247b72..64a85748d 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,6 @@ The SDK supports different features on the target platform: have the `curl` library available. On other platforms, library users need to implement their own transport, based on the `function transport` API. - **Crashpad Backend** is currently only supported on Linux, Windows and macOS. -- **Breakpad Backend** is currently only supported on Linux and Windows. ## Building and Installation @@ -179,6 +178,8 @@ using `cmake -D BUILD_SHARED_LIBS=OFF ..`. `OFF` will build `sentry` as a static library instead. If sentry is used as a subdirectory of another project, the value `BUILD_SHARED_LIBS` will be inherited by default. + When using `sentry` as a static library, make sure to `#define SENTRY_BUILD_STATIC 1` before including the sentry header. + - `SENTRY_PIC` (Default: ON): By default, `sentry` is built as a position independent library. From b8c8792cf647bc9cbec603e77656a454e70f05f3 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Wed, 21 Apr 2021 12:15:22 +0200 Subject: [PATCH 13/49] fix: Better macOS availability checks (#524) This should allow building on older macOS versions as well as running on older versions by fixing the usage of __builtin_available, and adding a different clock source for older macOS versions. --- CHANGELOG.md | 1 + src/sentry_boot.h | 4 ++++ src/sentry_utils.c | 8 +++---- src/sentry_utils.h | 23 +++++++++++++++++++++ src/unwinder/sentry_unwinder_libbacktrace.c | 14 ++++++------- 5 files changed, 38 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f5775e5c..4c51a39b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ - Build fixes for PPC and universal macOS. - Fixes to build using musl libc. - Correctness fixes around printf and strftime usage. +- Allow building and running on older macOS versions. **Internal**: diff --git a/src/sentry_boot.h b/src/sentry_boot.h index 3636f7806..2231366c0 100644 --- a/src/sentry_boot.h +++ b/src/sentry_boot.h @@ -33,6 +33,10 @@ # include #endif +#ifndef __has_builtin +# define __has_builtin(x) 0 +#endif + #include #include diff --git a/src/sentry_utils.c b/src/sentry_utils.c index 5134b56c9..8c877a023 100644 --- a/src/sentry_utils.c +++ b/src/sentry_utils.c @@ -1,13 +1,13 @@ // According to http://lua-users.org/lists/lua-l/2016-04/msg00216.html we can // use `stdtod_l` on all platforms when defining `_GNU_SOURCE`. -#define _GNU_SOURCE +#include "sentry_boot.h" -#include "sentry_utils.h" #include "sentry_alloc.h" #include "sentry_core.h" #include "sentry_string.h" #include "sentry_sync.h" +#include "sentry_utils.h" #include #include #include @@ -15,7 +15,7 @@ #include #include -#ifdef SENTRY_PLATFORM_MACOS +#ifdef SENTRY_PLATFORM_DARWIN # include #elif defined(SENTRY_PLATFORM_LINUX) && !defined(SENTRY_PLATFORM_ANDROID) # include "../vendor/stb_sprintf.h" @@ -493,7 +493,7 @@ sentry__snprintf_c(char *buf, size_t buf_size, const char *fmt, ...) rv = _vsnprintf_l(buf, buf_size, fmt, c_locale(), args); #elif defined(SENTRY_PLATFORM_ANDROID) || defined(SENTRY_PLATFORM_IOS) rv = vsnprintf(buf, buf_size, fmt, args); -#elif defined(SENTRY_PLATFORM_MACOS) +#elif defined(SENTRY_PLATFORM_DARWIN) rv = vsnprintf_l(buf, buf_size, c_locale(), fmt, args); #else rv = stbsp_vsnprintf(buf, buf_size, fmt, args); diff --git a/src/sentry_utils.h b/src/sentry_utils.h index 27a67e1f4..cb79c08f1 100644 --- a/src/sentry_utils.h +++ b/src/sentry_utils.h @@ -3,6 +3,10 @@ #include "sentry_boot.h" +#ifdef SENTRY_PLATFORM_DARWIN +# include +# include +#endif #ifdef SENTRY_PLATFORM_WINDOWS # include #else @@ -145,6 +149,25 @@ sentry__monotonic_time(void) LARGE_INTEGER qpc_counter; QueryPerformanceCounter(&qpc_counter); return qpc_counter.QuadPart * 1000 / qpc_frequency.QuadPart; +#elif defined(SENTRY_PLATFORM_DARWIN) + +// try `clock_gettime` first if available, +// fall back to `host_get_clock_service` otherwise +# if defined(MAC_OS_X_VERSION_10_12) && __has_builtin(__builtin_available) + if (__builtin_available(macOS 10.12, *)) { + struct timespec tv; + return (clock_gettime(CLOCK_MONOTONIC, &tv) == 0) + ? (uint64_t)tv.tv_sec * 1000 + tv.tv_nsec / 1000000 + : 0; + } +# endif + + clock_serv_t cclock; + mach_timespec_t mts; + host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); + clock_get_time(cclock, &mts); + mach_port_deallocate(mach_task_self(), cclock); + return (uint64_t)mts.tv_sec * 1000 + mts.tv_nsec / 1000000; #else struct timespec tv; return (clock_gettime(CLOCK_MONOTONIC, &tv) == 0) diff --git a/src/unwinder/sentry_unwinder_libbacktrace.c b/src/unwinder/sentry_unwinder_libbacktrace.c index 7b6261b1c..2c5c3b20b 100644 --- a/src/unwinder/sentry_unwinder_libbacktrace.c +++ b/src/unwinder/sentry_unwinder_libbacktrace.c @@ -1,27 +1,25 @@ #include "sentry_boot.h" -#if defined(SENTRY_PLATFORM_MACOS) || defined(__GLIBC__) +#if defined(SENTRY_PLATFORM_DARWIN) || defined(__GLIBC__) # include #endif -#ifndef __has_builtin -# define __has_builtin(x) 0 -#endif - size_t sentry__unwind_stack_libbacktrace( void *addr, const sentry_ucontext_t *uctx, void **ptrs, size_t max_frames) { if (addr) { -#if defined(SENTRY_PLATFORM_MACOS) && __has_builtin(__builtin_available) - if (__builtin_available(macOS 10.14, *)) +#if defined(SENTRY_PLATFORM_MACOS) && defined(MAC_OS_X_VERSION_10_14) \ + && __has_builtin(__builtin_available) + if (__builtin_available(macOS 10.14, *)) { return (size_t)backtrace_from_fp(addr, ptrs, (int)max_frames); + } #endif return 0; } else if (uctx) { return 0; } -#if defined(SENTRY_PLATFORM_MACOS) || defined(__GLIBC__) +#if defined(SENTRY_PLATFORM_DARWIN) || defined(__GLIBC__) return (size_t)backtrace(ptrs, (int)max_frames); #else (void)ptrs; From b3f2b6ddac886616b40cdccbb05b81af14b6adc9 Mon Sep 17 00:00:00 2001 From: getsentry-bot Date: Wed, 21 Apr 2021 10:27:17 +0000 Subject: [PATCH 14/49] release: 0.4.9 --- CHANGELOG.md | 2 +- include/sentry.h | 2 +- tests/assertions.py | 4 ++-- tests/test_integration_http.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c51a39b6..953d49303 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## Unreleased +## 0.4.9 **Features**: diff --git a/include/sentry.h b/include/sentry.h index 491a39f9f..91146520a 100644 --- a/include/sentry.h +++ b/include/sentry.h @@ -24,7 +24,7 @@ extern "C" { /* SDK Version */ #define SENTRY_SDK_NAME "sentry.native" -#define SENTRY_SDK_VERSION "0.4.8" +#define SENTRY_SDK_VERSION "0.4.9" #define SENTRY_SDK_USER_AGENT SENTRY_SDK_NAME "/" SENTRY_SDK_VERSION /* common platform detection */ diff --git a/tests/assertions.py b/tests/assertions.py index 6995aff54..5e11765f5 100644 --- a/tests/assertions.py +++ b/tests/assertions.py @@ -44,8 +44,8 @@ def assert_meta(envelope, release="test-example-release", integration=None): } expected_sdk = { "name": "sentry.native", - "version": "0.4.8", - "packages": [{"name": "github:getsentry/sentry-native", "version": "0.4.8"},], + "version": "0.4.9", + "packages": [{"name": "github:getsentry/sentry-native", "version": "0.4.9"},], } if not is_android: if sys.platform == "win32": diff --git a/tests/test_integration_http.py b/tests/test_integration_http.py index e32b2e922..334f9d8da 100644 --- a/tests/test_integration_http.py +++ b/tests/test_integration_http.py @@ -22,7 +22,7 @@ pytestmark = pytest.mark.skipif(not has_http, reason="tests need http") auth_header = ( - "Sentry sentry_key=uiaeosnrtdy, sentry_version=7, sentry_client=sentry.native/0.4.8" + "Sentry sentry_key=uiaeosnrtdy, sentry_version=7, sentry_client=sentry.native/0.4.9" ) From 756243a56980edfde96670bc33752a1d81912875 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Mon, 26 Apr 2021 10:09:08 +0200 Subject: [PATCH 15/49] fix: Avoid double-free on invalid DSN (#527) --- src/sentry_utils.c | 1 + tests/unit/test_utils.c | 15 +++++++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/sentry_utils.c b/src/sentry_utils.c index 8c877a023..88b1b6a30 100644 --- a/src/sentry_utils.c +++ b/src/sentry_utils.c @@ -209,6 +209,7 @@ sentry__url_cleanup(sentry_url_t *url) sentry_free(url->fragment); sentry_free(url->username); sentry_free(url->password); + memset(url, 0, sizeof(sentry_url_t)); } sentry_dsn_t * diff --git a/tests/unit/test_utils.c b/tests/unit/test_utils.c index 7d9736b99..db36abe5d 100644 --- a/tests/unit/test_utils.c +++ b/tests/unit/test_utils.c @@ -93,11 +93,18 @@ SENTRY_TEST(dsn_parsing_invalid) sentry_dsn_t *dsn = sentry__dsn_new("http://username:password@example.com/foo/bar?x=y#z"); TEST_CHECK(!!dsn); - if (!dsn) { - return; + if (dsn) { + TEST_CHECK(!dsn->is_valid); + sentry__dsn_decref(dsn); + } + + dsn = sentry__dsn_new("=https://foo@bar.ingest.sentry.io/" + "1234567"); + TEST_CHECK(!!dsn); + if (dsn) { + TEST_CHECK(!dsn->is_valid); + sentry__dsn_decref(dsn); } - TEST_CHECK(!dsn->is_valid); - sentry__dsn_decref(dsn); } SENTRY_TEST(dsn_store_url_with_path) From 3fdaa5d5eeb306fa2d13ca66ec25e656960d19b8 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Mon, 3 May 2021 10:28:41 +0200 Subject: [PATCH 16/49] meta: Use correct libunwindstack commit --- external/libunwindstack-ndk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/external/libunwindstack-ndk b/external/libunwindstack-ndk index 08419958d..abcfef9e7 160000 --- a/external/libunwindstack-ndk +++ b/external/libunwindstack-ndk @@ -1 +1 @@ -Subproject commit 08419958d963276c154e570203a342878f50ca41 +Subproject commit abcfef9e77ec6c8b0fe179591e354b8509727686 From f2ed3aca4c12597bb6ef71db3d874416722385b6 Mon Sep 17 00:00:00 2001 From: bschatt <44769431+bschatt@users.noreply.github.com> Date: Tue, 18 May 2021 11:42:56 +0200 Subject: [PATCH 17/49] fix: Allow for Unity builds (#536) --- src/transports/sentry_disk_transport.c | 5 +++-- src/transports/sentry_function_transport.c | 5 +++-- tests/unit/main.c | 2 +- tests/unit/sentry_testsupport.h | 2 +- tests/unit/test_attachments.c | 7 ++++--- tests/unit/test_basic.c | 9 +++++---- 6 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/transports/sentry_disk_transport.c b/src/transports/sentry_disk_transport.c index 2ccf448d6..3f0d03329 100644 --- a/src/transports/sentry_disk_transport.c +++ b/src/transports/sentry_disk_transport.c @@ -7,7 +7,7 @@ #include "sentry_string.h" static void -send_envelope(sentry_envelope_t *envelope, void *state) +send_envelope_disk_transport(sentry_envelope_t *envelope, void *state) { const sentry_run_t *run = state; @@ -18,7 +18,8 @@ send_envelope(sentry_envelope_t *envelope, void *state) sentry_transport_t * sentry_new_disk_transport(const sentry_run_t *run) { - sentry_transport_t *transport = sentry_transport_new(send_envelope); + sentry_transport_t *transport + = sentry_transport_new(send_envelope_disk_transport); if (!transport) { return NULL; } diff --git a/src/transports/sentry_function_transport.c b/src/transports/sentry_function_transport.c index d51e29e05..57aa14db2 100644 --- a/src/transports/sentry_function_transport.c +++ b/src/transports/sentry_function_transport.c @@ -10,7 +10,7 @@ struct transport_state { }; static void -send_envelope(sentry_envelope_t *envelope, void *_state) +send_envelope_function_transport(sentry_envelope_t *envelope, void *_state) { struct transport_state *state = _state; state->func(envelope, state->data); @@ -29,7 +29,8 @@ sentry_new_function_transport( state->func = func; state->data = data; - sentry_transport_t *transport = sentry_transport_new(send_envelope); + sentry_transport_t *transport + = sentry_transport_new(send_envelope_function_transport); if (!transport) { sentry_free(state); return NULL; diff --git a/tests/unit/main.c b/tests/unit/main.c index e38f4d16f..fb351549f 100644 --- a/tests/unit/main.c +++ b/tests/unit/main.c @@ -2,7 +2,7 @@ #include "sentry_testsupport.h" -#define XX(Name) void CONCAT(test_sentry_, Name)(void); +#define XX(Name) SENTRY_TEST(Name); #include "tests.inc" #undef XX diff --git a/tests/unit/sentry_testsupport.h b/tests/unit/sentry_testsupport.h index 0a7b1f518..81927db1b 100644 --- a/tests/unit/sentry_testsupport.h +++ b/tests/unit/sentry_testsupport.h @@ -13,7 +13,7 @@ #include "../vendor/acutest.h" #define CONCAT(A, B) A##B -#define SENTRY_TEST(Name) void CONCAT(test_sentry_, Name)(void **UNUSED(state)) +#define SENTRY_TEST(Name) void CONCAT(test_sentry_, Name)(void) #define SKIP_TEST() (void)0 #define TEST_CHECK_STRING_EQUAL(Val, ReferenceVal) \ diff --git a/tests/unit/test_attachments.c b/tests/unit/test_attachments.c index de22327a9..4b3a6bbdd 100644 --- a/tests/unit/test_attachments.c +++ b/tests/unit/test_attachments.c @@ -10,7 +10,7 @@ typedef struct { } sentry_attachments_testdata_t; static void -send_envelope(const sentry_envelope_t *envelope, void *_data) +send_envelope_test_attachments(const sentry_envelope_t *envelope, void *_data) { sentry_attachments_testdata_t *data = _data; data->called += 1; @@ -33,8 +33,9 @@ SENTRY_TEST(lazy_attachments) sentry_options_t *options = sentry_options_new(); sentry_options_set_auto_session_tracking(options, false); sentry_options_set_dsn(options, "https://foo@sentry.invalid/42"); - sentry_options_set_transport( - options, sentry_new_function_transport(send_envelope, &testdata)); + sentry_options_set_transport(options, + sentry_new_function_transport( + send_envelope_test_attachments, &testdata)); sentry_options_set_release(options, "prod"); sentry_options_add_attachment(options, PREFIX ".existing-file-attachment"); diff --git a/tests/unit/test_basic.c b/tests/unit/test_basic.c index 0f68b2165..342ef0037 100644 --- a/tests/unit/test_basic.c +++ b/tests/unit/test_basic.c @@ -3,7 +3,7 @@ #include static void -send_envelope(const sentry_envelope_t *envelope, void *data) +send_envelope_test_basic(const sentry_envelope_t *envelope, void *data) { uint64_t *called = data; *called += 1; @@ -33,8 +33,8 @@ SENTRY_TEST(basic_function_transport) sentry_options_t *options = sentry_options_new(); sentry_options_set_dsn(options, "https://foo@sentry.invalid/42"); - sentry_options_set_transport( - options, sentry_new_function_transport(send_envelope, &called)); + sentry_options_set_transport(options, + sentry_new_function_transport(send_envelope_test_basic, &called)); sentry_options_set_release(options, "prod"); sentry_options_set_require_user_consent(options, true); sentry_init(options); @@ -80,7 +80,8 @@ SENTRY_TEST(sampling_before_send) sentry_options_t *options = sentry_options_new(); sentry_options_set_dsn(options, "https://foo@sentry.invalid/42"); sentry_options_set_transport(options, - sentry_new_function_transport(send_envelope, &called_transport)); + sentry_new_function_transport( + send_envelope_test_basic, &called_transport)); sentry_options_set_before_send(options, before_send, &called_beforesend); sentry_options_set_sample_rate(options, 0.75); sentry_init(options); From d45871c0603d83386376b2b7d34ac456a608f35c Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Thu, 20 May 2021 13:36:16 +0200 Subject: [PATCH 18/49] ref: Add more testcases that trigger crashes in various ways (#538) --- examples/example.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/examples/example.c b/examples/example.c index 5b75f749c..2ed6ae971 100644 --- a/examples/example.c +++ b/examples/example.c @@ -9,11 +9,16 @@ #include #include #include +#ifdef NDEBUG +# undef NDEBUG +#endif +#include #ifdef SENTRY_PLATFORM_WINDOWS # include # define sleep_s(SECONDS) Sleep((SECONDS)*1000) #else +# include # include # define sleep_s(SECONDS) sleep(SECONDS) #endif @@ -156,6 +161,20 @@ main(int argc, char **argv) if (has_arg(argc, argv, "crash")) { trigger_crash(); } + if (has_arg(argc, argv, "assert")) { + assert(0); + } + if (has_arg(argc, argv, "abort")) { + abort(); + } +#ifdef SENTRY_PLATFORM_UNIX + if (has_arg(argc, argv, "raise")) { + raise(SIGSEGV); + } + if (has_arg(argc, argv, "kill")) { + kill(getpid(), SIGSEGV); + } +#endif if (has_arg(argc, argv, "capture-event")) { sentry_value_t event = sentry_value_new_message_event( @@ -180,6 +199,7 @@ main(int argc, char **argv) // make sure everything flushes sentry_close(); + if (has_arg(argc, argv, "sleep-after-shutdown")) { sleep_s(1); } From b16af1c862771784f1ef1a9e5eee05e37b0ba823 Mon Sep 17 00:00:00 2001 From: Burak Yigit Kaya Date: Fri, 4 Jun 2021 11:03:14 +0300 Subject: [PATCH 19/49] ref(craft): Modernize Craft config (#543) --- .craft.yml | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/.craft.yml b/.craft.yml index c9f27fa1d..6908dce36 100644 --- a/.craft.yml +++ b/.craft.yml @@ -1,29 +1,18 @@ -minVersion: "0.15.0" -github: - owner: getsentry - repo: sentry-native +minVersion: 0.23.1 changelogPolicy: auto - -statusProvider: - name: github -artifactProvider: - name: github - targets: - name: github - name: registry - type: sdk - config: - canonical: "github:getsentry/sentry-native" + sdks: + github:getsentry/sentry-native: - name: gcs bucket: sentry-sdk-assets paths: - path: /sentry-native/{{version}}/ metadata: - cacheControl: "public, max-age=2592000" + cacheControl: public, max-age=2592000 - path: /sentry-native/latest/ metadata: - cacheControl: "public, max-age=600" - + cacheControl: public, max-age=600 requireNames: - /^sentry-native.zip$/ From 09c3a9e06782071d87f00c912734e3222ee89dc6 Mon Sep 17 00:00:00 2001 From: MikeRumplerSentry <85497711+MikeRumplerSentry@users.noreply.github.com> Date: Tue, 15 Jun 2021 15:41:09 +0200 Subject: [PATCH 20/49] fix: Update venv and test-discovery Makefile targets (#544) --- Makefile | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 7ca1c06a1..cf14522f9 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ all: test update-test-discovery: - @perl -ne 'print if s/SENTRY_TEST\(([^)]+)\)/XX(\1)/' tests/unit/*.c | sort | uniq > tests/unit/tests.inc + @perl -ne 'print if s/SENTRY_TEST\(([^)]+)\)/XX(\1)/' tests/unit/*.c | sort | grep -v define | uniq > tests/unit/tests.inc .PHONY: update-test-discovery build/Makefile: CMakeLists.txt @@ -55,8 +55,7 @@ setup-venv: .venv/bin/python .venv/bin/python: Makefile tests/requirements.txt @rm -rf .venv - @which virtualenv || sudo pip install virtualenv - virtualenv -p python3 .venv + python3 -m venv .venv .venv/bin/pip install --upgrade --requirement tests/requirements.txt format: setup-venv From ed7f98fa2b2cd1309535d0f40898ed853f7e19ea Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Fri, 18 Jun 2021 12:19:27 +0200 Subject: [PATCH 21/49] fix: Avoid recursion when using `sentry_reinstall_backend` (#548) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, the `inproc` and `crashpad` (on linux) backends didn’t correctly reset their signal handlers when doing `reinstall_backend` (or multiple `init` calls for that matter). This has led to an infinite loop generating crashes. The fix now correctly unregisters the inproc/crashpad signal handlers, and adds an integration test using `reinstall_backend` to make sure we do not end up in an infinite loop. Co-authored-by: Mischa Alff --- examples/example.c | 4 +++ src/backends/sentry_backend_crashpad.cpp | 30 +++++++++++++++++++ src/backends/sentry_backend_inproc.c | 1 + tests/requirements.txt | 2 +- tests/test_integration_crashpad.py | 20 +++++++++++++ tests/test_integration_http.py | 37 ++++++++++++++++++++++++ 6 files changed, 93 insertions(+), 1 deletion(-) diff --git a/examples/example.c b/examples/example.c index 2ed6ae971..1c1275975 100644 --- a/examples/example.c +++ b/examples/example.c @@ -154,6 +154,10 @@ main(int argc, char **argv) } } + if (has_arg(argc, argv, "reinstall")) { + sentry_reinstall_backend(); + } + if (has_arg(argc, argv, "sleep")) { sleep_s(10); } diff --git a/src/backends/sentry_backend_crashpad.cpp b/src/backends/sentry_backend_crashpad.cpp index 06e028934..f92e62ddc 100644 --- a/src/backends/sentry_backend_crashpad.cpp +++ b/src/backends/sentry_backend_crashpad.cpp @@ -44,6 +44,27 @@ extern "C" { #ifdef SENTRY_PLATFORM_LINUX # define SIGNAL_STACK_SIZE 65536 static stack_t g_signal_stack; + +# include "util/posix/signals.h" + +// This list was taken from crashpad's util/posix/signals.cc file +// and is used to know which signals we need to reset to default +// when shutting down the backend +constexpr int g_CrashSignals[] = { + SIGABRT, + SIGBUS, + SIGFPE, + SIGILL, + SIGQUIT, + SIGSEGV, + SIGSYS, + SIGTRAP, +# if defined(SIGEMT) + SIGEMT, +# endif // defined(SIGEMT) + SIGXCPU, + SIGXFSZ, +}; #endif typedef struct { @@ -281,6 +302,15 @@ sentry__crashpad_backend_startup( static void sentry__crashpad_backend_shutdown(sentry_backend_t *backend) { +#ifdef SENTRY_PLATFORM_LINUX + // restore signal handlers to their default state + for (const auto signal : g_CrashSignals) { + if (crashpad::Signals::IsCrashSignal(signal)) { + crashpad::Signals::InstallDefaultHandler(signal); + } + } +#endif + crashpad_state_t *data = (crashpad_state_t *)backend->data; delete data->db; data->db = nullptr; diff --git a/src/backends/sentry_backend_inproc.c b/src/backends/sentry_backend_inproc.c index d3b77fd40..c15493dab 100644 --- a/src/backends/sentry_backend_inproc.c +++ b/src/backends/sentry_backend_inproc.c @@ -111,6 +111,7 @@ shutdown_inproc_backend(sentry_backend_t *UNUSED(backend)) sigaltstack(&g_signal_stack, 0); sentry_free(g_signal_stack.ss_sp); g_signal_stack.ss_sp = NULL; + reset_signal_handlers(); } #elif defined SENTRY_PLATFORM_WINDOWS diff --git a/tests/requirements.txt b/tests/requirements.txt index 892a12bc4..440b71546 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -1,3 +1,3 @@ black==19.10b0 pytest==5.4.1 -pytest-httpserver==0.3.4 +pytest-httpserver==1.0.0 diff --git a/tests/test_integration_crashpad.py b/tests/test_integration_crashpad.py index 8358bed7d..ba56b6ee2 100644 --- a/tests/test_integration_crashpad.py +++ b/tests/test_integration_crashpad.py @@ -29,6 +29,23 @@ def test_crashpad_capture(cmake, httpserver): assert len(httpserver.log) == 2 +def test_crashpad_reinstall(cmake, httpserver): + tmp_path = cmake(["sentry_example"], {"SENTRY_BACKEND": "crashpad"}) + + env = dict(os.environ, SENTRY_DSN=make_dsn(httpserver)) + httpserver.expect_oneshot_request("/api/123456/minidump/").respond_with_data("OK") + + with httpserver.wait(timeout=10) as waiting: + child = run(tmp_path, "sentry_example", ["log", "reinstall", "crash"], env=env) + assert child.returncode # well, its a crash after all + + assert waiting.result + + run(tmp_path, "sentry_example", ["log", "no-setup"], check=True, env=env) + + assert len(httpserver.log) == 1 + + def test_crashpad_crash(cmake, httpserver): tmp_path = cmake(["sentry_example"], {"SENTRY_BACKEND": "crashpad"}) @@ -67,6 +84,9 @@ def test_crashpad_crash(cmake, httpserver): assert_crashpad_upload(multipart) +@pytest.mark.skipif( + sys.platform == "linux", reason="linux clears the signal handlers on shutdown" +) def test_crashpad_crash_after_shutdown(cmake, httpserver): tmp_path = cmake(["sentry_example"], {"SENTRY_BACKEND": "crashpad"}) diff --git a/tests/test_integration_http.py b/tests/test_integration_http.py index 334f9d8da..3066368c4 100644 --- a/tests/test_integration_http.py +++ b/tests/test_integration_http.py @@ -216,6 +216,24 @@ def test_inproc_crash_http(cmake, httpserver): assert_crash(envelope) +def test_inproc_reinstall(cmake, httpserver): + tmp_path = cmake(["sentry_example"], {"SENTRY_BACKEND": "inproc"}) + + env = dict(os.environ, SENTRY_DSN=make_dsn(httpserver)) + httpserver.expect_request( + "/api/123456/envelope/", headers={"x-sentry-auth": auth_header}, + ).respond_with_data("OK") + + child = run(tmp_path, "sentry_example", ["log", "reinstall", "crash"], env=env,) + assert child.returncode # well, its a crash after all + + run( + tmp_path, "sentry_example", ["log", "no-setup"], check=True, env=env, + ) + + assert len(httpserver.log) == 1 + + def test_inproc_dump_inflight(cmake, httpserver): tmp_path = cmake(["sentry_example"], {"SENTRY_BACKEND": "inproc"}) @@ -268,6 +286,25 @@ def test_breakpad_crash_http(cmake, httpserver): assert_minidump(envelope) +@pytest.mark.skipif(not has_breakpad, reason="test needs breakpad backend") +def test_breakpad_reinstall(cmake, httpserver): + tmp_path = cmake(["sentry_example"], {"SENTRY_BACKEND": "breakpad"}) + + env = dict(os.environ, SENTRY_DSN=make_dsn(httpserver)) + httpserver.expect_request( + "/api/123456/envelope/", headers={"x-sentry-auth": auth_header}, + ).respond_with_data("OK") + + child = run(tmp_path, "sentry_example", ["log", "reinstall", "crash"], env=env,) + assert child.returncode # well, its a crash after all + + run( + tmp_path, "sentry_example", ["log", "no-setup"], check=True, env=env, + ) + + assert len(httpserver.log) == 1 + + @pytest.mark.skipif(not has_breakpad, reason="test needs breakpad backend") def test_breakpad_dump_inflight(cmake, httpserver): tmp_path = cmake(["sentry_example"], {"SENTRY_BACKEND": "breakpad"}) From c2fe4b0a6865ea1b9cb25c89475307d9b9600928 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Janiszewski?= Date: Sat, 19 Jun 2021 18:53:36 +0200 Subject: [PATCH 22/49] fix: Address -Wundef warning for SENTRY_UNITTEST defines (#549) --- src/modulefinder/sentry_modulefinder_linux.h | 2 +- src/sentry_core.c | 2 +- src/sentry_envelope.c | 2 +- src/sentry_envelope.h | 2 +- src/sentry_ratelimiter.c | 2 +- src/sentry_ratelimiter.h | 2 +- src/sentry_unix_pageallocator.c | 2 +- src/sentry_unix_pageallocator.h | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/modulefinder/sentry_modulefinder_linux.h b/src/modulefinder/sentry_modulefinder_linux.h index 71654521c..5204158c0 100644 --- a/src/modulefinder/sentry_modulefinder_linux.h +++ b/src/modulefinder/sentry_modulefinder_linux.h @@ -29,7 +29,7 @@ typedef struct { uint8_t num_mappings; } sentry_module_t; -#if SENTRY_UNITTEST +#ifdef SENTRY_UNITTEST bool sentry__procmaps_read_ids_from_elf( sentry_value_t value, const sentry_module_t *module); diff --git a/src/sentry_core.c b/src/sentry_core.c index 704d8549c..3774b99d8 100644 --- a/src/sentry_core.c +++ b/src/sentry_core.c @@ -434,7 +434,7 @@ sentry_handle_exception(const sentry_ucontext_t *uctx) sentry_uuid_t sentry__new_event_id(void) { -#if SENTRY_UNITTEST +#ifdef SENTRY_UNITTEST return sentry_uuid_from_string("4c035723-8638-4c3a-923f-2ab9d08b4018"); #else return sentry_uuid_new_v4(); diff --git a/src/sentry_envelope.c b/src/sentry_envelope.c index 7bd621c0d..71cef6ed6 100644 --- a/src/sentry_envelope.c +++ b/src/sentry_envelope.c @@ -410,7 +410,7 @@ sentry_envelope_write_to_file( return rv; } -#if SENTRY_UNITTEST +#ifdef SENTRY_UNITTEST size_t sentry__envelope_get_item_count(const sentry_envelope_t *envelope) { diff --git a/src/sentry_envelope.h b/src/sentry_envelope.h index efd31a771..ac5c69136 100644 --- a/src/sentry_envelope.h +++ b/src/sentry_envelope.h @@ -86,7 +86,7 @@ MUST_USE int sentry_envelope_write_to_path( const sentry_envelope_t *envelope, const sentry_path_t *path); // these for now are only needed for tests -#if SENTRY_UNITTEST +#ifdef SENTRY_UNITTEST size_t sentry__envelope_get_item_count(const sentry_envelope_t *envelope); const sentry_envelope_item_t *sentry__envelope_get_item( const sentry_envelope_t *envelope, size_t idx); diff --git a/src/sentry_ratelimiter.c b/src/sentry_ratelimiter.c index 025f69707..e1dd8c855 100644 --- a/src/sentry_ratelimiter.c +++ b/src/sentry_ratelimiter.c @@ -99,7 +99,7 @@ sentry__rate_limiter_free(sentry_rate_limiter_t *rl) sentry_free(rl); } -#if SENTRY_UNITTEST +#ifdef SENTRY_UNITTEST uint64_t sentry__rate_limiter_get_disabled_until( const sentry_rate_limiter_t *rl, int category) diff --git a/src/sentry_ratelimiter.h b/src/sentry_ratelimiter.h index 0a17754d3..58a666121 100644 --- a/src/sentry_ratelimiter.h +++ b/src/sentry_ratelimiter.h @@ -41,7 +41,7 @@ bool sentry__rate_limiter_update_from_http_retry_after( bool sentry__rate_limiter_is_disabled( const sentry_rate_limiter_t *rl, int category); -#if SENTRY_UNITTEST +#ifdef SENTRY_UNITTEST /** * The rate limiters state is completely opaque. Unless in tests, where we would * want to actually peek into the specific rate limiting `category`. diff --git a/src/sentry_unix_pageallocator.c b/src/sentry_unix_pageallocator.c index 09653a60d..0226c9b09 100644 --- a/src/sentry_unix_pageallocator.c +++ b/src/sentry_unix_pageallocator.c @@ -125,7 +125,7 @@ sentry__page_allocator_alloc(size_t size) return rv; } -#if SENTRY_UNITTEST +#ifdef SENTRY_UNITTEST void sentry__page_allocator_disable(void) { diff --git a/src/sentry_unix_pageallocator.h b/src/sentry_unix_pageallocator.h index 290192bc0..efc5b68b3 100644 --- a/src/sentry_unix_pageallocator.h +++ b/src/sentry_unix_pageallocator.h @@ -21,7 +21,7 @@ void sentry__page_allocator_enable(void); */ void *sentry__page_allocator_alloc(size_t size); -#if SENTRY_UNITTEST +#ifdef SENTRY_UNITTEST /** * This disables the page allocator, which invalidates every allocation that was * done through it. Therefore it is only safe to use in unit tests From 415621e50ec7f909dc10490488eebe3325c40c82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Janiszewski?= Date: Sat, 19 Jun 2021 18:53:53 +0200 Subject: [PATCH 23/49] build: Set 32-bit option for compiling assembly as well (#550) This fixes compilation of breakpad for 32-bit systems --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8a74a93ac..696d270ae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -70,6 +70,7 @@ if(LINUX) if(SENTRY_BUILD_FORCE32) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32 -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32 -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE") + set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -m32 -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE") set_property(GLOBAL PROPERTY FIND_LIBRARY_USE_LIB64_PATHS OFF) endif() endif() From c2bddac3e455c9a5cf60428d58849d108114d74e Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Tue, 22 Jun 2021 13:08:38 +0200 Subject: [PATCH 24/49] meta: Update break/crashpad to 2021-06-14 (#552) --- external/breakpad | 2 +- external/crashpad | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/external/breakpad b/external/breakpad index bbc999925..ba407b2ff 160000 --- a/external/breakpad +++ b/external/breakpad @@ -1 +1 @@ -Subproject commit bbc999925cadc1b40f153f1a695062a88be5891a +Subproject commit ba407b2ff475e93a8d325ae20e8777befd0c5c43 diff --git a/external/crashpad b/external/crashpad index fdbe4d7b1..5cf3032b2 160000 --- a/external/crashpad +++ b/external/crashpad @@ -1 +1 @@ -Subproject commit fdbe4d7b1053939f2c77193d8e854de5a3c93e17 +Subproject commit 5cf3032b2281cf0928acc8bccf69f91ccf26b939 From 0391fb09618a7ea4f7f7cb998d979113ebee8a29 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Tue, 22 Jun 2021 13:32:48 +0200 Subject: [PATCH 25/49] fix: Shorten/Split Locked sections to avoid deadlock (#551) We have received a report that the `sentry_get_modules_list` on mac can deadlock when other code concurrently does a `dlopen` and thus invokes the `add_image` callback from a different thread. We shorten/split the locked blocks in order to avoid holding a lock in the `get_modules` function whenever the `add_image` function is being invoked possibly from other threads. --- src/modulefinder/sentry_modulefinder_apple.c | 42 ++++++++++++-------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/src/modulefinder/sentry_modulefinder_apple.c b/src/modulefinder/sentry_modulefinder_apple.c index 96dc1a70d..77f9608e1 100644 --- a/src/modulefinder/sentry_modulefinder_apple.c +++ b/src/modulefinder/sentry_modulefinder_apple.c @@ -43,11 +43,8 @@ add_image(const struct mach_header *mh, intptr_t UNUSED(vmaddr_slide)) return; } - sentry__mutex_lock(&g_mutex); - - sentry_value_t modules = g_modules; - sentry_value_t new_modules = sentry__value_clone(modules); sentry_value_t module = sentry_value_new_object(); + sentry_value_set_by_key(module, "type", sentry_value_new_string("macho")); sentry_value_set_by_key( module, "code_file", sentry_value_new_string(info.dli_fname)); sentry_value_set_by_key( @@ -80,7 +77,10 @@ add_image(const struct mach_header *mh, intptr_t UNUSED(vmaddr_slide)) } } - sentry_value_set_by_key(module, "type", sentry_value_new_string("macho")); + sentry__mutex_lock(&g_mutex); + + sentry_value_t modules = g_modules; + sentry_value_t new_modules = sentry__value_clone(modules); sentry_value_append(new_modules, module); sentry_value_freeze(new_modules); sentry_value_decref(g_modules); @@ -92,6 +92,12 @@ add_image(const struct mach_header *mh, intptr_t UNUSED(vmaddr_slide)) static void remove_image(const struct mach_header *mh, intptr_t UNUSED(vmaddr_slide)) { + const platform_mach_header *header = (const platform_mach_header *)(mh); + Dl_info info; + if (!dladdr(header, &info)) { + return; + } + sentry__mutex_lock(&g_mutex); if (sentry_value_is_null(g_modules) @@ -99,12 +105,6 @@ remove_image(const struct mach_header *mh, intptr_t UNUSED(vmaddr_slide)) goto done; } - const platform_mach_header *header = (const platform_mach_header *)(mh); - Dl_info info; - if (!dladdr(header, &info)) { - goto done; - } - char ref_addr[100]; snprintf(ref_addr, sizeof(ref_addr), "0x%llx", (long long)info.dli_fbase); sentry_value_t new_modules = sentry_value_new_list(); @@ -130,18 +130,28 @@ remove_image(const struct mach_header *mh, intptr_t UNUSED(vmaddr_slide)) sentry_value_t sentry_get_modules_list(void) { + // We have 2 locked blocks here (actually 3, with the one inside of the + // `add_image` callback). We do that because we have observed deadlocks when + // code concurrently `dlopen`s and thus invokes the `add_image` callback + // from a different thread. sentry__mutex_lock(&g_mutex); if (!g_initialized) { g_modules = sentry_value_new_list(); + g_initialized = true; + + sentry__mutex_unlock(&g_mutex); + // TODO: maybe use `_dyld_image_count` and `_dyld_get_image_header`? - // Those functions are documented to not be thread-safe, though using - // the `register_X` functions are also unsafe because they lack a - // corresponding `unregister` function, and will thus crash when sentry - // itself is unloaded. + // Those functions are documented to not be thread-safe, though + // using the `register_X` functions are also unsafe because they + // lack a corresponding `unregister` function, and will thus crash + // when sentry itself is unloaded. _dyld_register_func_for_add_image(add_image); _dyld_register_func_for_remove_image(remove_image); - g_initialized = true; + + sentry__mutex_lock(&g_mutex); } + sentry_value_t modules = g_modules; sentry_value_incref(modules); sentry__mutex_unlock(&g_mutex); From 91bd5ec532cc0c8405565e17aa976e2a5f9ca513 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Wed, 23 Jun 2021 11:46:05 +0200 Subject: [PATCH 26/49] fix: Tighten Stack Usage (#553) According to some docs, JVM/JNI stacks on Android can be as small as 32K, and our own sigaltstack is not much larger with 64K. Make sure to avoid large stack allocations as much as possible. We have especially seen the literal content of `/proc/self/maps` as well as formatted addresses inside corrupted release/environment attributes, which might point to overflows that write into a previously allocated release/environment string. --- src/modulefinder/sentry_modulefinder_apple.c | 2 +- src/modulefinder/sentry_modulefinder_linux.c | 21 ++++++++++------ src/sentry_json.c | 2 +- src/sentry_os.c | 4 +-- src/sentry_string.c | 26 ++++++++++++++++---- src/sentry_string.h | 12 +++++++++ src/sentry_utils.c | 2 +- src/sentry_value.c | 4 +-- 8 files changed, 54 insertions(+), 19 deletions(-) diff --git a/src/modulefinder/sentry_modulefinder_apple.c b/src/modulefinder/sentry_modulefinder_apple.c index 77f9608e1..93957e0cb 100644 --- a/src/modulefinder/sentry_modulefinder_apple.c +++ b/src/modulefinder/sentry_modulefinder_apple.c @@ -105,7 +105,7 @@ remove_image(const struct mach_header *mh, intptr_t UNUSED(vmaddr_slide)) goto done; } - char ref_addr[100]; + char ref_addr[32]; snprintf(ref_addr, sizeof(ref_addr), "0x%llx", (long long)info.dli_fbase); sentry_value_t new_modules = sentry_value_new_list(); diff --git a/src/modulefinder/sentry_modulefinder_linux.c b/src/modulefinder/sentry_modulefinder_linux.c index 513c22165..9ba91e28d 100644 --- a/src/modulefinder/sentry_modulefinder_linux.c +++ b/src/modulefinder/sentry_modulefinder_linux.c @@ -435,25 +435,32 @@ load_modules(sentry_value_t modules) return; } - // just read the whole map at once, maybe do it line-by-line as a followup… - char buf[4096]; + // Read the whole map at once. Doing it line-by-line would be a good + // followup. sentry_stringbuilder_t sb; sentry__stringbuilder_init(&sb); while (true) { + char *buf = sentry__stringbuilder_reserve(&sb, 4096); + if (!buf) { + sentry__stringbuilder_cleanup(&sb); + close(fd); + return; + } ssize_t n = read(fd, buf, 4096); if (n < 0 && (errno == EAGAIN || errno == EINTR)) { continue; } else if (n <= 0) { break; } - if (sentry__stringbuilder_append_buf(&sb, buf, n)) { - sentry__stringbuilder_cleanup(&sb); - close(fd); - return; - } + sentry__stringbuilder_set_len(&sb, sentry__stringbuilder_len(&sb) + n); } close(fd); + // ensure the buffer is zero terminated + if (sentry__stringbuilder_append(&sb, "")) { + sentry__stringbuilder_cleanup(&sb); + return; + } char *contents = sentry__stringbuilder_into_string(&sb); if (!contents) { return; diff --git a/src/sentry_json.c b/src/sentry_json.c index 378725e45..316868256 100644 --- a/src/sentry_json.c +++ b/src/sentry_json.c @@ -194,7 +194,7 @@ void sentry__jsonwriter_write_double(sentry_jsonwriter_t *jw, double val) { if (can_write_item(jw)) { - char buf[50]; + char buf[24]; // The MAX_SAFE_INTEGER is 9007199254740991, which has 16 digits int written = sentry__snprintf_c(buf, sizeof(buf), "%.16g", val); // print `null` if we have printf issues or a non-finite double, which diff --git a/src/sentry_os.c b/src/sentry_os.c index c4479bc65..977261eba 100644 --- a/src/sentry_os.c +++ b/src/sentry_os.c @@ -34,7 +34,7 @@ sentry__get_os_context(void) } ffi->dwFileFlags &= ffi->dwFileFlagsMask; - char buf[100]; + char buf[32]; snprintf(buf, sizeof(buf), "%u.%u.%u", ffi->dwFileVersionMS >> 16, ffi->dwFileVersionMS & 0xffff, ffi->dwFileVersionLS >> 16); @@ -71,7 +71,7 @@ sentry__get_os_context(void) sentry_value_set_by_key(os, "name", sentry_value_new_string("macOS")); - char buf[100]; + char buf[32]; size_t buf_len = sizeof(buf); if (sysctlbyname("kern.osproductversion", buf, &buf_len, NULL, 0) != 0) { diff --git a/src/sentry_string.c b/src/sentry_string.c index a13eacc96..a1581c752 100644 --- a/src/sentry_string.c +++ b/src/sentry_string.c @@ -13,10 +13,10 @@ sentry__stringbuilder_init(sentry_stringbuilder_t *sb) sb->len = 0; } -static int -append(sentry_stringbuilder_t *sb, const char *s, size_t len) +char * +sentry__stringbuilder_reserve(sentry_stringbuilder_t *sb, size_t len) { - size_t needed = sb->len + len + 1; + size_t needed = sb->len + len; if (!sb->buf || needed > sb->allocated) { size_t new_alloc_size = sb->allocated; if (new_alloc_size == 0) { @@ -27,7 +27,7 @@ append(sentry_stringbuilder_t *sb, const char *s, size_t len) } char *new_buf = sentry_malloc(new_alloc_size); if (!new_buf) { - return 1; + return NULL; } if (sb->buf) { memcpy(new_buf, sb->buf, sb->allocated); @@ -36,7 +36,17 @@ append(sentry_stringbuilder_t *sb, const char *s, size_t len) sb->buf = new_buf; sb->allocated = new_alloc_size; } - memcpy(sb->buf + sb->len, s, len); + return &sb->buf[sb->len]; +} + +static int +append(sentry_stringbuilder_t *sb, const char *s, size_t len) +{ + char *buf = sentry__stringbuilder_reserve(sb, len + 1); + if (!buf) { + return 1; + } + memcpy(buf, s, len); sb->len += len; // make sure we're always zero terminated @@ -105,6 +115,12 @@ sentry__stringbuilder_len(const sentry_stringbuilder_t *sb) return sb->len; } +void +sentry__stringbuilder_set_len(sentry_stringbuilder_t *sb, size_t len) +{ + sb->len = len; +} + char * sentry__string_clone(const char *str) { diff --git a/src/sentry_string.h b/src/sentry_string.h index c537b5e3e..feeff3678 100644 --- a/src/sentry_string.h +++ b/src/sentry_string.h @@ -73,6 +73,18 @@ void sentry__stringbuilder_cleanup(sentry_stringbuilder_t *sb); */ size_t sentry__stringbuilder_len(const sentry_stringbuilder_t *sb); +/** + * Resizes the stringbuilder buffer to make sure there is at least `len` bytes + * available at the end, and returns a pointer *to the reservation*. + */ +char *sentry__stringbuilder_reserve(sentry_stringbuilder_t *sb, size_t len); + +/** + * Sets the number of used bytes in the string builder, to be used together with + * `sentry__stringbuilder_reserve` to avoid copying from an intermediate buffer. + */ +void sentry__stringbuilder_set_len(sentry_stringbuilder_t *sb, size_t len); + /** * Duplicates a zero terminated string. */ diff --git a/src/sentry_utils.c b/src/sentry_utils.c index 88b1b6a30..8e3300871 100644 --- a/src/sentry_utils.c +++ b/src/sentry_utils.c @@ -358,7 +358,7 @@ sentry__dsn_get_minidump_url(const sentry_dsn_t *dsn) char * sentry__msec_time_to_iso8601(uint64_t time) { - char buf[255]; + char buf[64]; size_t buf_len = sizeof(buf); time_t secs = time / 1000; struct tm *tm; diff --git a/src/sentry_value.c b/src/sentry_value.c index 7a14a980f..668f0dc1e 100644 --- a/src/sentry_value.c +++ b/src/sentry_value.c @@ -544,7 +544,7 @@ sentry__value_stringify(sentry_value_t value) case SENTRY_VALUE_TYPE_STRING: return sentry__string_clone(sentry_value_as_string(value)); default: { - char buf[50]; + char buf[24]; size_t written = (size_t)sentry__snprintf_c( buf, sizeof(buf), "%g", sentry_value_as_double(value)); if (written >= sizeof(buf)) { @@ -934,7 +934,7 @@ sentry__value_new_string_from_wstr(const wchar_t *s) sentry_value_t sentry__value_new_addr(uint64_t addr) { - char buf[100]; + char buf[32]; size_t written = (size_t)snprintf( buf, sizeof(buf), "0x%llx", (unsigned long long)addr); if (written >= sizeof(buf)) { From fa31ff7bd7af9ba7154ccf7cf5bf246628d94fc9 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Thu, 24 Jun 2021 09:16:00 +0200 Subject: [PATCH 27/49] meta: Update Changelog (#556) --- CHANGELOG.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 953d49303..ea34c3825 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,27 @@ # Changelog +## Unreleased + +**Fixes**: + +- Fix a potential deadlock in macOS modulefinder. +- Lower Stack usage, to lower change of stack overflows. +- Avoid a double-free when parsing an invalid DSN. +- Improvements to Unity Builds and 32-bit Builds. +- Fix infinite recursion in signal handler by correctly cleaning up on shutdown. + +**Internal**: + +- Update Crashpad and Breakpad submodules to 2021-06-14. + +**Thank you**: + +Features, fixes and improvements in this release have been contributed by: + +- [@janisozaur](https://github.com/janisozaur) +- [@bschatt](https://github.com/bschatt) +- [@GenuineAster](https://github.com/GenuineAster) + ## 0.4.9 **Features**: From 2e98ec1184e020b7c4555833cd1ff1e6cc6aa00b Mon Sep 17 00:00:00 2001 From: getsentry-bot Date: Thu, 24 Jun 2021 07:16:43 +0000 Subject: [PATCH 28/49] release: 0.4.10 --- CHANGELOG.md | 2 +- include/sentry.h | 2 +- tests/assertions.py | 4 ++-- tests/test_integration_http.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea34c3825..bbfc80f82 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## Unreleased +## 0.4.10 **Fixes**: diff --git a/include/sentry.h b/include/sentry.h index 91146520a..fb57588ee 100644 --- a/include/sentry.h +++ b/include/sentry.h @@ -24,7 +24,7 @@ extern "C" { /* SDK Version */ #define SENTRY_SDK_NAME "sentry.native" -#define SENTRY_SDK_VERSION "0.4.9" +#define SENTRY_SDK_VERSION "0.4.10" #define SENTRY_SDK_USER_AGENT SENTRY_SDK_NAME "/" SENTRY_SDK_VERSION /* common platform detection */ diff --git a/tests/assertions.py b/tests/assertions.py index 5e11765f5..0f4634562 100644 --- a/tests/assertions.py +++ b/tests/assertions.py @@ -44,8 +44,8 @@ def assert_meta(envelope, release="test-example-release", integration=None): } expected_sdk = { "name": "sentry.native", - "version": "0.4.9", - "packages": [{"name": "github:getsentry/sentry-native", "version": "0.4.9"},], + "version": "0.4.10", + "packages": [{"name": "github:getsentry/sentry-native", "version": "0.4.10"},], } if not is_android: if sys.platform == "win32": diff --git a/tests/test_integration_http.py b/tests/test_integration_http.py index 3066368c4..7a5007c06 100644 --- a/tests/test_integration_http.py +++ b/tests/test_integration_http.py @@ -22,7 +22,7 @@ pytestmark = pytest.mark.skipif(not has_http, reason="tests need http") auth_header = ( - "Sentry sentry_key=uiaeosnrtdy, sentry_version=7, sentry_client=sentry.native/0.4.9" + "Sentry sentry_key=uiaeosnrtdy, sentry_version=7, sentry_client=sentry.native/0.4.10" ) From bf2d9343f29a091c70d51ce4bac8866539cb65ae Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Thu, 24 Jun 2021 10:31:51 +0200 Subject: [PATCH 29/49] reformat --- tests/test_integration_http.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/test_integration_http.py b/tests/test_integration_http.py index 7a5007c06..9f4827c6a 100644 --- a/tests/test_integration_http.py +++ b/tests/test_integration_http.py @@ -21,9 +21,7 @@ pytestmark = pytest.mark.skipif(not has_http, reason="tests need http") -auth_header = ( - "Sentry sentry_key=uiaeosnrtdy, sentry_version=7, sentry_client=sentry.native/0.4.10" -) +auth_header = "Sentry sentry_key=uiaeosnrtdy, sentry_version=7, sentry_client=sentry.native/0.4.10" def test_capture_http(cmake, httpserver): From 959b81e18edec529a4245952c0c387b66b327ab8 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Tue, 29 Jun 2021 09:47:44 +0200 Subject: [PATCH 30/49] fix: Make Linux modulefinder/unwinder safer (#559) This is using the `process_vm_read` call to safely poke at random memory. It also makes sure to shim the libc provided call with a direct syscall for older Android devices. --- .github/workflows/ci.yml | 8 +- external/libunwindstack-ndk | 2 +- src/modulefinder/sentry_modulefinder_linux.c | 196 ++++++++++++------- 3 files changed, 135 insertions(+), 71 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c9872eb82..ba2e73596 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -79,14 +79,14 @@ jobs: os: windows-latest # The Android emulator is currently only available on macos, see: # https://docs.microsoft.com/en-us/azure/devops/pipelines/ecosystems/android?view=azure-devops#test-on-the-android-emulator - - name: Android (API 16, NDK 19) + - name: Android (API 16, NDK 20) os: macOs-latest ANDROID_API: 16 - ANDROID_NDK: 19.2.5345600 - - name: Android (API 30 NDK 21) + ANDROID_NDK: 20.1.5948944 + - name: Android (API 30, NDK 22) os: macOs-latest ANDROID_API: 30 - ANDROID_NDK: 21.3.6528147 + ANDROID_NDK: 22.1.7171670 name: ${{ matrix.name }} runs-on: ${{ matrix.os }} diff --git a/external/libunwindstack-ndk b/external/libunwindstack-ndk index abcfef9e7..a4c27d48d 160000 --- a/external/libunwindstack-ndk +++ b/external/libunwindstack-ndk @@ -1 +1 @@ -Subproject commit abcfef9e77ec6c8b0fe179591e354b8509727686 +Subproject commit a4c27d48deff95fe922fe9733ef5c1339bdbf4fb diff --git a/src/modulefinder/sentry_modulefinder_linux.c b/src/modulefinder/sentry_modulefinder_linux.c index 9ba91e28d..9629912b0 100644 --- a/src/modulefinder/sentry_modulefinder_linux.c +++ b/src/modulefinder/sentry_modulefinder_linux.c @@ -1,3 +1,6 @@ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif #include "sentry_modulefinder_linux.h" #include "sentry_core.h" @@ -10,9 +13,22 @@ #include #include #include +#include #include +#include #include +#if defined(__ANDROID_API__) && __ANDROID_API__ < 23 +static ssize_t +process_vm_readv(pid_t __pid, const struct iovec *__local_iov, + unsigned long __local_iov_count, const struct iovec *__remote_iov, + unsigned long __remote_iov_count, unsigned long __flags) +{ + return syscall(__NR_process_vm_readv, __pid, __local_iov, __local_iov_count, + __remote_iov, __remote_iov_count, __flags); +} +#endif + #define MIN(a, b) ((a) < (b) ? (a) : (b)) #define ENSURE(Ptr) \ @@ -51,6 +67,51 @@ sentry__module_get_addr( return NULL; } +/** + * Reads `size` bytes from `src` to `dst` safely without segfaulting in case + * `src` is not readable. + */ +static bool +read_safely(void *dst, void *src, size_t size) +{ + struct iovec local[1]; + struct iovec remote[1]; + + pid_t pid = getpid(); + local[0].iov_base = dst; + local[0].iov_len = size; + remote[0].iov_base = src; + remote[0].iov_len = size; + + errno = 0; + ssize_t nread = process_vm_readv(pid, local, 1, remote, 1, 0); + bool rv = nread == (ssize_t)size; + + // The syscall is only available in Linux 3.2, meaning Android 17. + // If that is the case, just fall back to an unsafe memcpy. +#if defined(__ANDROID_API__) && __ANDROID_API__ < 17 + if (!rv && errno == EINVAL) { + memcpy(dst, src, size); + rv = true; + } +#endif + return rv; +} + +/** + * Reads `size` bytes into `dst`, from the `start_offset` inside the `module`. + */ +static bool +sentry__module_read_safely(void *dst, const sentry_module_t *module, + uint64_t start_offset, uint64_t size) +{ + void *src = sentry__module_get_addr(module, start_offset, size); + if (!src) { + return false; + } + return read_safely(dst, src, (size_t)size); +} + static void sentry__module_mapping_push( sentry_module_t *module, const sentry_parsed_module_t *parsed) @@ -183,51 +244,50 @@ get_code_id_from_elf(const sentry_module_t *module, size_t *size_out) *size_out = 0; // iterate over all the program headers, for 32/64 bit separately - const unsigned char *e_ident - = sentry__module_get_addr(module, 0, EI_NIDENT); - ENSURE(e_ident); + unsigned char e_ident[EI_NIDENT]; + ENSURE(sentry__module_read_safely(e_ident, module, 0, EI_NIDENT)); if (e_ident[EI_CLASS] == ELFCLASS64) { - const Elf64_Ehdr *elf - = sentry__module_get_addr(module, 0, sizeof(Elf64_Ehdr)); - ENSURE(elf); - for (int i = 0; i < elf->e_phnum; i++) { - const Elf64_Phdr *header = sentry__module_get_addr( - module, elf->e_phoff + elf->e_phentsize * i, elf->e_phentsize); - ENSURE(header); + Elf64_Ehdr elf; + ENSURE(sentry__module_read_safely(&elf, module, 0, sizeof(Elf64_Ehdr))); + + for (int i = 0; i < elf.e_phnum; i++) { + Elf64_Phdr header; + ENSURE(sentry__module_read_safely(&header, module, + elf.e_phoff + elf.e_phentsize * i, sizeof(Elf64_Phdr))); // we are only interested in notes - if (header->p_type != PT_NOTE) { + if (header.p_type != PT_NOTE) { continue; } void *segment_addr = sentry__module_get_addr( - module, header->p_offset, header->p_filesz); + module, header.p_offset, header.p_filesz); ENSURE(segment_addr); - const uint8_t *code_id = get_code_id_from_notes(header->p_align, + const uint8_t *code_id = get_code_id_from_notes(header.p_align, segment_addr, - (void *)((uintptr_t)segment_addr + header->p_filesz), size_out); + (void *)((uintptr_t)segment_addr + header.p_filesz), size_out); if (code_id) { return code_id; } } } else { - const Elf32_Ehdr *elf - = sentry__module_get_addr(module, 0, sizeof(Elf32_Ehdr)); - ENSURE(elf); - - for (int i = 0; i < elf->e_phnum; i++) { - const Elf32_Phdr *header = sentry__module_get_addr( - module, elf->e_phoff + elf->e_phentsize * i, elf->e_phentsize); - ENSURE(header); + Elf32_Ehdr elf; + ENSURE(sentry__module_read_safely(&elf, module, 0, sizeof(Elf32_Ehdr))); + + for (int i = 0; i < elf.e_phnum; i++) { + Elf32_Phdr header; + ENSURE(sentry__module_read_safely(&header, module, + elf.e_phoff + elf.e_phentsize * i, sizeof(Elf32_Phdr))); + // we are only interested in notes - if (header->p_type != PT_NOTE) { + if (header.p_type != PT_NOTE) { continue; } void *segment_addr = sentry__module_get_addr( - module, header->p_offset, header->p_filesz); + module, header.p_offset, header.p_filesz); ENSURE(segment_addr); - const uint8_t *code_id = get_code_id_from_notes(header->p_align, + const uint8_t *code_id = get_code_id_from_notes(header.p_align, segment_addr, - (void *)((uintptr_t)segment_addr + header->p_filesz), size_out); + (void *)((uintptr_t)segment_addr + header.p_filesz), size_out); if (code_id) { return code_id; } @@ -244,56 +304,57 @@ get_code_id_from_text_fallback(const sentry_module_t *module) size_t text_size = 0; // iterate over all the program headers, for 32/64 bit separately - const unsigned char *e_ident - = sentry__module_get_addr(module, 0, EI_NIDENT); - ENSURE(e_ident); + unsigned char e_ident[EI_NIDENT]; + ENSURE(sentry__module_read_safely(e_ident, module, 0, EI_NIDENT)); if (e_ident[EI_CLASS] == ELFCLASS64) { - const Elf64_Ehdr *elf - = sentry__module_get_addr(module, 0, sizeof(Elf64_Ehdr)); - ENSURE(elf); - const Elf64_Shdr *strheader = sentry__module_get_addr(module, - elf->e_shoff + elf->e_shentsize * elf->e_shstrndx, - elf->e_shentsize); - ENSURE(strheader); + Elf64_Ehdr elf; + ENSURE(sentry__module_read_safely(&elf, module, 0, sizeof(Elf64_Ehdr))); + + Elf64_Shdr strheader; + ENSURE(sentry__module_read_safely(&strheader, module, + elf.e_shoff + elf.e_shentsize * elf.e_shstrndx, + sizeof(Elf64_Shdr))); const char *names = sentry__module_get_addr( - module, strheader->sh_offset, strheader->sh_entsize); + module, strheader.sh_offset, strheader.sh_entsize); ENSURE(names); - for (int i = 0; i < elf->e_shnum; i++) { - const Elf64_Shdr *header = sentry__module_get_addr( - module, elf->e_shoff + elf->e_shentsize * i, elf->e_shentsize); - ENSURE(header); - const char *name = names + header->sh_name; - if (header->sh_type == SHT_PROGBITS && strcmp(name, ".text") == 0) { + for (int i = 0; i < elf.e_shnum; i++) { + Elf64_Shdr header; + ENSURE(sentry__module_read_safely(&header, module, + elf.e_shoff + elf.e_shentsize * i, sizeof(Elf64_Shdr))); + + const char *name = names + header.sh_name; + if (header.sh_type == SHT_PROGBITS && strcmp(name, ".text") == 0) { text = sentry__module_get_addr( - module, header->sh_offset, header->sh_size); + module, header.sh_offset, header.sh_size); ENSURE(text); - text_size = header->sh_size; + text_size = header.sh_size; break; } } } else { - const Elf32_Ehdr *elf - = sentry__module_get_addr(module, 0, sizeof(Elf64_Ehdr)); - ENSURE(elf); - const Elf32_Shdr *strheader = sentry__module_get_addr(module, - elf->e_shoff + elf->e_shentsize * elf->e_shstrndx, - elf->e_shentsize); - ENSURE(strheader); + Elf32_Ehdr elf; + ENSURE(sentry__module_read_safely(&elf, module, 0, sizeof(Elf32_Ehdr))); + + Elf32_Shdr strheader; + ENSURE(sentry__module_read_safely(&strheader, module, + elf.e_shoff + elf.e_shentsize * elf.e_shstrndx, + sizeof(Elf32_Shdr))); const char *names = sentry__module_get_addr( - module, strheader->sh_offset, strheader->sh_entsize); + module, strheader.sh_offset, strheader.sh_entsize); ENSURE(names); - for (int i = 0; i < elf->e_shnum; i++) { - const Elf32_Shdr *header = sentry__module_get_addr( - module, elf->e_shoff + elf->e_shentsize * i, elf->e_shentsize); - ENSURE(header); - const char *name = names + header->sh_name; - if (header->sh_type == SHT_PROGBITS && strcmp(name, ".text") == 0) { + for (int i = 0; i < elf.e_shnum; i++) { + Elf32_Shdr header; + ENSURE(sentry__module_read_safely(&header, module, + elf.e_shoff + elf.e_shentsize * i, sizeof(Elf32_Shdr))); + + const char *name = names + header.sh_name; + if (header.sh_type == SHT_PROGBITS && strcmp(name, ".text") == 0) { text = sentry__module_get_addr( - module, header->sh_offset, header->sh_size); + module, header.sh_offset, header.sh_size); ENSURE(text); - text_size = header->sh_size; + text_size = header.sh_size; break; } } @@ -420,9 +481,10 @@ get_linux_vdso(void) static bool is_valid_elf_header(void *start) { - // we try to interpret `addr` as an ELF file, which should start with a - // magic number... - const unsigned char *e_ident = start; + unsigned char e_ident[EI_NIDENT]; + if (!read_safely(e_ident, start, EI_NIDENT)) { + return false; + } return e_ident[EI_MAG0] == ELFMAG0 && e_ident[EI_MAG1] == ELFMAG1 && e_ident[EI_MAG2] == ELFMAG2 && e_ident[EI_MAG3] == ELFMAG3; } @@ -471,9 +533,11 @@ load_modules(sentry_value_t modules) // we have multiple memory maps per file, and we need to merge their offsets // based on the filename. Luckily, the maps are ordered by filename, so yay - sentry_module_t last_module = { 0 }; + sentry_module_t last_module; + memset(&last_module, 0, sizeof(sentry_module_t)); while (true) { - sentry_parsed_module_t module = { 0 }; + sentry_parsed_module_t module; + memset(&module, 0, sizeof(sentry_parsed_module_t)); int read = sentry__procmaps_parse_module_line(current_line, &module); current_line += read; if (!read) { From 05db566dd329ca44a85e4ac9fc7b722899a7c483 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Wed, 30 Jun 2021 08:59:09 +0200 Subject: [PATCH 31/49] docs: Try to better explain unwind API (#564) --- include/sentry.h | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/include/sentry.h b/include/sentry.h index fb57588ee..2fbe7b279 100644 --- a/include/sentry.h +++ b/include/sentry.h @@ -469,9 +469,12 @@ typedef struct sentry_ucontext_s { * * If the address is given in `addr` the stack is unwound form there. * Otherwise (NULL is passed) the current instruction pointer is used as - * start address. The stack trace is written to `stacktrace_out` with up to - * `max_len` frames being written. The actual number of unwound stackframes - * is returned. + * start address. + * Unwinding with a given `addr` is not supported on all platforms. + * + * The stack trace in the form of instruction-addresses, is written to the + * caller allocated `stacktrace_out`, with up to `max_len` frames being written. + * The actual number of unwound stackframes is returned. */ SENTRY_EXPERIMENTAL_API size_t sentry_unwind_stack( void *addr, void **stacktrace_out, size_t max_len); @@ -479,8 +482,12 @@ SENTRY_EXPERIMENTAL_API size_t sentry_unwind_stack( /** * Unwinds the stack from the given context. * - * The stack trace is written to `stacktrace_out` with up to `max_len` frames - * being written. The actual number of unwound stackframes is returned. + * The caller is responsible to construct an appropriate `sentry_ucontext_t`. + * Unwinding from a user context is not supported on all platforms. + * + * The stack trace in the form of instruction-addresses, is written to the + * caller allocated `stacktrace_out`, with up to `max_len` frames being written. + * The actual number of unwound stackframes is returned. */ SENTRY_EXPERIMENTAL_API size_t sentry_unwind_stack_from_ucontext( const sentry_ucontext_t *uctx, void **stacktrace_out, size_t max_len); From c3b7b07c99d19f39a4910093378e29bba86fde37 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Thu, 1 Jul 2021 10:58:47 +0200 Subject: [PATCH 32/49] fix: Make Crashpad Backend respect max_breadcrumbs setting (#566) --- src/backends/sentry_backend_crashpad.cpp | 14 +++++++++----- src/sentry_backend.h | 3 ++- src/sentry_core.c | 3 ++- src/sentry_value.c | 12 +++++++++--- tests/unit/test_value.c | 9 +++++++++ 5 files changed, 31 insertions(+), 10 deletions(-) diff --git a/src/backends/sentry_backend_crashpad.cpp b/src/backends/sentry_backend_crashpad.cpp index f92e62ddc..dcebd9c7e 100644 --- a/src/backends/sentry_backend_crashpad.cpp +++ b/src/backends/sentry_backend_crashpad.cpp @@ -324,16 +324,20 @@ sentry__crashpad_backend_shutdown(sentry_backend_t *backend) } static void -sentry__crashpad_backend_add_breadcrumb( - sentry_backend_t *backend, sentry_value_t breadcrumb) +sentry__crashpad_backend_add_breadcrumb(sentry_backend_t *backend, + sentry_value_t breadcrumb, const sentry_options_t *options) { crashpad_state_t *data = (crashpad_state_t *)backend->data; - bool first_breadcrumb = data->num_breadcrumbs % SENTRY_BREADCRUMBS_MAX == 0; + size_t max_breadcrumbs = options->max_breadcrumbs; + if (!max_breadcrumbs) { + return; + } + + bool first_breadcrumb = data->num_breadcrumbs % max_breadcrumbs == 0; const sentry_path_t *breadcrumb_file - = data->num_breadcrumbs % (SENTRY_BREADCRUMBS_MAX * 2) - < SENTRY_BREADCRUMBS_MAX + = data->num_breadcrumbs % (max_breadcrumbs * 2) < max_breadcrumbs ? data->breadcrumb1_path : data->breadcrumb2_path; data->num_breadcrumbs++; diff --git a/src/sentry_backend.h b/src/sentry_backend.h index 02faf3303..bb954272a 100644 --- a/src/sentry_backend.h +++ b/src/sentry_backend.h @@ -21,7 +21,8 @@ struct sentry_backend_s { sentry_backend_t *, const sentry_options_t *options); // NOTE: The breadcrumb is not moved into the hook and does not need to be // `decref`-d internally. - void (*add_breadcrumb_func)(sentry_backend_t *, sentry_value_t breadcrumb); + void (*add_breadcrumb_func)(sentry_backend_t *, sentry_value_t breadcrumb, + const sentry_options_t *options); void (*user_consent_changed_func)(sentry_backend_t *); uint64_t (*get_last_crash_func)(sentry_backend_t *); void *data; diff --git a/src/sentry_core.c b/src/sentry_core.c index 3774b99d8..ba1b54c69 100644 --- a/src/sentry_core.c +++ b/src/sentry_core.c @@ -480,7 +480,8 @@ sentry_add_breadcrumb(sentry_value_t breadcrumb) SENTRY_WITH_OPTIONS (options) { if (options->backend && options->backend->add_breadcrumb_func) { // the hook will *not* take ownership - options->backend->add_breadcrumb_func(options->backend, breadcrumb); + options->backend->add_breadcrumb_func( + options->backend, breadcrumb, options); } max_breadcrumbs = options->max_breadcrumbs; } diff --git a/src/sentry_value.c b/src/sentry_value.c index 668f0dc1e..0fb51df69 100644 --- a/src/sentry_value.c +++ b/src/sentry_value.c @@ -611,13 +611,19 @@ sentry__value_append_bounded(sentry_value_t value, sentry_value_t v, size_t max) // move 99 items (len - 1) // from 20 - size_t to_move = max - 1; + size_t to_move = max >= 1 ? max - 1 : 0; size_t to_shift = l->len - to_move; for (size_t i = 0; i < to_shift; i++) { sentry_value_decref(l->items[i]); } - memmove(l->items, l->items + (to_shift), to_move * sizeof(l->items[0])); - l->items[max - 1] = v; + if (to_move) { + memmove(l->items, l->items + to_shift, to_move * sizeof(l->items[0])); + } + if (max >= 1) { + l->items[max - 1] = v; + } else { + sentry_value_decref(v); + } l->len = max; return 0; diff --git a/tests/unit/test_value.c b/tests/unit/test_value.c index 1863241fc..783567f40 100644 --- a/tests/unit/test_value.c +++ b/tests/unit/test_value.c @@ -477,6 +477,15 @@ SENTRY_TEST(value_collections_leak) TEST_CHECK_INT_EQUAL(sentry_value_refcount(obj), 3); + sentry_value_incref(obj); + sentry__value_append_bounded(list, obj, 1); + TEST_CHECK_INT_EQUAL(sentry_value_refcount(obj), 2); + + sentry_value_incref(obj); + sentry__value_append_bounded(list, obj, 0); + TEST_CHECK_INT_EQUAL(sentry_value_refcount(obj), 1); + TEST_CHECK_INT_EQUAL(sentry_value_get_length(list), 0); + sentry_value_decref(list); TEST_CHECK_INT_EQUAL(sentry_value_refcount(obj), 1); From ad2e1423c30ca04fc17ce31f894fb42f80bc2843 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Tue, 6 Jul 2021 12:49:09 +0200 Subject: [PATCH 33/49] fix: Cancel slow winhttp requests on shutdown (#570) Co-authored-by: Gerhard Herbert --- src/sentry_sync.c | 1 + src/transports/sentry_transport_winhttp.c | 49 +++++++++++++++++------ 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/src/sentry_sync.c b/src/sentry_sync.c index ade4e369e..f652f6654 100644 --- a/src/sentry_sync.c +++ b/src/sentry_sync.c @@ -325,6 +325,7 @@ sentry__bgworker_shutdown(sentry_bgworker_t *bgw, uint64_t timeout) uint64_t now = sentry__monotonic_time(); if (now > started && now - started > timeout) { + sentry__atomic_fetch_and_add(&bgw->running, -1); sentry__mutex_unlock(&bgw->task_lock); SENTRY_WARN( "background thread failed to shut down cleanly within timeout"); diff --git a/src/transports/sentry_transport_winhttp.c b/src/transports/sentry_transport_winhttp.c index 516d0ec16..afb6e7c52 100644 --- a/src/transports/sentry_transport_winhttp.c +++ b/src/transports/sentry_transport_winhttp.c @@ -20,6 +20,7 @@ typedef struct { sentry_rate_limiter_t *ratelimiter; HINTERNET session; HINTERNET connect; + HINTERNET request; bool debug; } winhttp_bgworker_state_t; @@ -110,7 +111,30 @@ static int sentry__winhttp_transport_shutdown(uint64_t timeout, void *transport_state) { sentry_bgworker_t *bgworker = (sentry_bgworker_t *)transport_state; - return sentry__bgworker_shutdown(bgworker, timeout); + winhttp_bgworker_state_t *state = sentry__bgworker_get_state(bgworker); + + int rv = sentry__bgworker_shutdown(bgworker, timeout); + if (rv != 0) { + // Seems like some requests are taking too long/hanging + // Just close them to make sure the background thread is exiting. + if (state->connect) { + WinHttpCloseHandle(state->connect); + state->connect = NULL; + } + + // NOTE: We need to close the session before closing the request. + // This will cancel all other requests which might be queued as well. + if (state->session) { + WinHttpCloseHandle(state->session); + state->session = NULL; + } + if (state->request) { + WinHttpCloseHandle(state->request); + state->request = NULL; + } + } + + return rv; } static void @@ -129,7 +153,6 @@ sentry__winhttp_send_task(void *_envelope, void *_state) wchar_t *url = sentry__string_to_wstr(req->url); wchar_t *headers = NULL; - HINTERNET request = NULL; URL_COMPONENTS url_components; wchar_t hostname[128]; @@ -152,10 +175,10 @@ sentry__winhttp_send_task(void *_envelope, void *_state) } bool is_secure = strstr(req->url, "https") == req->url; - request = WinHttpOpenRequest(state->connect, L"POST", + state->request = WinHttpOpenRequest(state->connect, L"POST", url_components.lpszUrlPath, NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, is_secure ? WINHTTP_FLAG_SECURE : 0); - if (!request) { + if (!state->request) { SENTRY_WARNF( "`WinHttpOpenRequest` failed with code `%d`", GetLastError()); goto exit; @@ -178,16 +201,16 @@ sentry__winhttp_send_task(void *_envelope, void *_state) SENTRY_TRACEF( "sending request using winhttp to \"%s\":\n%S", req->url, headers); - if (WinHttpSendRequest(request, headers, (DWORD)-1, (LPVOID)req->body, - (DWORD)req->body_len, (DWORD)req->body_len, 0)) { - WinHttpReceiveResponse(request, NULL); + if (WinHttpSendRequest(state->request, headers, (DWORD)-1, + (LPVOID)req->body, (DWORD)req->body_len, (DWORD)req->body_len, 0)) { + WinHttpReceiveResponse(state->request, NULL); if (state->debug) { // this is basically the example from: // https://docs.microsoft.com/en-us/windows/win32/api/winhttp/nf-winhttp-winhttpqueryheaders#examples DWORD dwSize = 0; LPVOID lpOutBuffer = NULL; - WinHttpQueryHeaders(request, WINHTTP_QUERY_RAW_HEADERS_CRLF, + WinHttpQueryHeaders(state->request, WINHTTP_QUERY_RAW_HEADERS_CRLF, WINHTTP_HEADER_NAME_BY_INDEX, NULL, &dwSize, WINHTTP_NO_HEADER_INDEX); @@ -197,7 +220,7 @@ sentry__winhttp_send_task(void *_envelope, void *_state) // Now, use WinHttpQueryHeaders to retrieve the header. if (lpOutBuffer - && WinHttpQueryHeaders(request, + && WinHttpQueryHeaders(state->request, WINHTTP_QUERY_RAW_HEADERS_CRLF, WINHTTP_HEADER_NAME_BY_INDEX, lpOutBuffer, &dwSize, WINHTTP_NO_HEADER_INDEX)) { @@ -211,7 +234,7 @@ sentry__winhttp_send_task(void *_envelope, void *_state) // lets just assume we won’t have headers > 2k wchar_t buf[2048]; DWORD buf_size = sizeof(buf); - if (WinHttpQueryHeaders(request, WINHTTP_QUERY_CUSTOM, + if (WinHttpQueryHeaders(state->request, WINHTTP_QUERY_CUSTOM, L"x-sentry-rate-limits", buf, &buf_size, WINHTTP_NO_HEADER_INDEX)) { char *h = sentry__string_from_wstr(buf); @@ -219,7 +242,7 @@ sentry__winhttp_send_task(void *_envelope, void *_state) sentry__rate_limiter_update_from_header(state->ratelimiter, h); sentry_free(h); } - } else if (WinHttpQueryHeaders(request, WINHTTP_QUERY_CUSTOM, + } else if (WinHttpQueryHeaders(state->request, WINHTTP_QUERY_CUSTOM, L"retry-after", buf, &buf_size, WINHTTP_NO_HEADER_INDEX)) { char *h = sentry__string_from_wstr(buf); @@ -238,7 +261,9 @@ sentry__winhttp_send_task(void *_envelope, void *_state) SENTRY_TRACEF("request handled in %llums", now - started); exit: - if (request) { + if (state->request) { + HINTERNET request = state->request; + state->request = NULL; WinHttpCloseHandle(request); } sentry_free(url); From 5648480156c4c897613c6c60d02c404de35bd1a6 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Tue, 6 Jul 2021 16:18:49 +0200 Subject: [PATCH 34/49] fix: Properly close the background worker thread on timeout (#571) --- src/sentry_sync.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sentry_sync.c b/src/sentry_sync.c index f652f6654..d81b21628 100644 --- a/src/sentry_sync.c +++ b/src/sentry_sync.c @@ -284,11 +284,11 @@ int sentry__bgworker_start(sentry_bgworker_t *bgw) { SENTRY_TRACE("starting background worker thread"); - sentry__atomic_fetch_and_add(&bgw->running, 1); + sentry__atomic_store(&bgw->running, 1); // this incref moves the reference into the background thread sentry__bgworker_incref(bgw); if (sentry__thread_spawn(&bgw->thread_id, &worker_thread, bgw) != 0) { - sentry__atomic_fetch_and_add(&bgw->running, -1); + sentry__atomic_store(&bgw->running, 0); sentry__bgworker_decref(bgw); return 1; } @@ -299,7 +299,7 @@ static void shutdown_task(void *task_data, void *UNUSED(state)) { sentry_bgworker_t *bgw = task_data; - sentry__atomic_fetch_and_add(&bgw->running, -1); + sentry__atomic_store(&bgw->running, 0); } int @@ -325,11 +325,11 @@ sentry__bgworker_shutdown(sentry_bgworker_t *bgw, uint64_t timeout) uint64_t now = sentry__monotonic_time(); if (now > started && now - started > timeout) { - sentry__atomic_fetch_and_add(&bgw->running, -1); + sentry__atomic_store(&bgw->running, 0); + sentry__thread_detach(bgw->thread_id); sentry__mutex_unlock(&bgw->task_lock); SENTRY_WARN( "background thread failed to shut down cleanly within timeout"); - sentry__thread_detach(bgw->thread_id); return 1; } From b35eb96e9bcae3ac90cd651854a1793a6b8a9165 Mon Sep 17 00:00:00 2001 From: MikeRumplerSentry <85497711+MikeRumplerSentry@users.noreply.github.com> Date: Tue, 6 Jul 2021 16:47:13 +0200 Subject: [PATCH 35/49] fix: Possible race conditions in init/close and sessions (#545) --- .clang-format | 2 +- src/sentry_core.c | 65 ++++++++++++++++++----- src/sentry_core.h | 14 +++++ src/sentry_logger.h | 2 + src/sentry_options.h | 2 + src/sentry_scope.c | 37 +------------ src/sentry_scope.h | 17 ++---- src/sentry_session.c | 52 +++++++++++-------- src/sentry_session.h | 6 +-- src/sentry_sync.c | 19 +------ src/sentry_sync.h | 27 ++++++++-- tests/cmake.py | 2 + tests/unit/CMakeLists.txt | 1 + tests/unit/test_concurrency.c | 97 +++++++++++++++++++++++++++++++++++ tests/unit/test_session.c | 3 ++ tests/unit/tests.inc | 2 + 16 files changed, 242 insertions(+), 106 deletions(-) create mode 100644 tests/unit/test_concurrency.c diff --git a/.clang-format b/.clang-format index 56b761bb2..ba4b564d9 100644 --- a/.clang-format +++ b/.clang-format @@ -4,4 +4,4 @@ IndentPPDirectives: AfterHash ColumnLimit: 80 AlwaysBreakAfterDefinitionReturnType: All PointerAlignment: Right -ForEachMacros: ['SENTRY_WITH_SCOPE', 'SENTRY_WITH_SCOPE_MUT', 'SENTRY_WITH_SCOPE_MUT_NO_FLUSH', 'SENTRY_WITH_OPTIONS'] +ForEachMacros: ['SENTRY_WITH_SCOPE', 'SENTRY_WITH_SCOPE_MUT', 'SENTRY_WITH_SCOPE_MUT_NO_FLUSH', 'SENTRY_WITH_OPTIONS', 'SENTRY_WITH_OPTIONS_MUT'] diff --git a/src/sentry_core.c b/src/sentry_core.c index ba1b54c69..56f8e66b2 100644 --- a/src/sentry_core.c +++ b/src/sentry_core.c @@ -36,6 +36,19 @@ sentry__options_getref(void) return options; } +sentry_options_t * +sentry__options_lock(void) +{ + sentry__mutex_lock(&g_options_lock); + return g_options; +} + +void +sentry__options_unlock(void) +{ + sentry__mutex_unlock(&g_options_lock); +} + static void load_user_consent(sentry_options_t *opts) { @@ -72,6 +85,12 @@ sentry__should_skip_upload(void) int sentry_init(sentry_options_t *options) { + // this function is to be called only once, so we do not allow more than one + // caller + sentry__mutex_lock(&g_options_lock); + // pre-init here, so we can consistently use bailing out to :fail + sentry_transport_t *transport = NULL; + sentry_close(); sentry_logger_t logger = { NULL, NULL }; @@ -84,11 +103,10 @@ sentry_init(sentry_options_t *options) if (sentry__path_create_dir_all(options->database_path)) { SENTRY_WARN("failed to create database directory or there is no write " "access to this directory"); - sentry_options_free(options); - return 1; + goto fail; } - sentry_transport_t *transport = options->transport; + transport = options->transport; sentry_path_t *database_path = options->database_path; options->database_path = sentry__path_absolute(database_path); if (options->database_path) { @@ -139,9 +157,7 @@ sentry_init(sentry_options_t *options) last_crash = backend->get_last_crash_func(backend); } - sentry__mutex_lock(&g_options_lock); g_options = options; - sentry__mutex_unlock(&g_options_lock); // *after* setting the global options, trigger a scope and consent flush, // since at least crashpad needs that. @@ -167,6 +183,7 @@ sentry_init(sentry_options_t *options) sentry_start_session(); } + sentry__mutex_unlock(&g_options_lock); return 0; fail: @@ -175,21 +192,21 @@ sentry_init(sentry_options_t *options) sentry__transport_shutdown(transport, 0); } sentry_options_free(options); + sentry__mutex_unlock(&g_options_lock); return 1; } int sentry_close(void) { - sentry_end_session(); - + // this function is to be called only once, so we do not allow more than one + // caller sentry__mutex_lock(&g_options_lock); sentry_options_t *options = g_options; - g_options = NULL; - sentry__mutex_unlock(&g_options_lock); size_t dumped_envelopes = 0; if (options) { + sentry_end_session(); if (options->backend && options->backend->shutdown_func) { SENTRY_TRACE("shutting down backend"); options->backend->shutdown_func(options->backend); @@ -210,12 +227,17 @@ sentry_close(void) || !options->backend->can_capture_after_shutdown)) { sentry__run_clean(options->run); } - sentry_options_free(options); + } else { + SENTRY_DEBUG("sentry_close() called, but options was empty"); } + g_options = NULL; + sentry__mutex_unlock(&g_options_lock); + sentry__scope_cleanup(); sentry_clear_modulecache(); + return (int)dumped_envelopes; } @@ -343,7 +365,18 @@ sentry_capture_event(sentry_value_t event) was_captured = true; envelope = sentry__prepare_event(options, event, &event_id); if (envelope) { - sentry__add_current_session_to_envelope(envelope); + if (options->session) { + SENTRY_WITH_OPTIONS_MUT (mut_options) { + sentry__envelope_add_session( + envelope, mut_options->session); + // we're assuming that if a session is added to an envelope + // it will be sent onwards. This means we now need to set + // the init flag to false because we're no longer the + // initial session update. + mut_options->session->init = false; + } + } + sentry__capture_envelope(options->transport, envelope); } } @@ -460,10 +493,18 @@ sentry__ensure_event_id(sentry_value_t event, sentry_uuid_t *uuid_out) void sentry_set_user(sentry_value_t user) { + if (!sentry_value_is_null(user)) { + SENTRY_WITH_OPTIONS_MUT (options) { + if (options->session) { + sentry__session_sync_user(options->session, user); + sentry__run_write_session(options->run, options->session); + } + } + } + SENTRY_WITH_SCOPE_MUT (scope) { sentry_value_decref(scope->user); scope->user = user; - sentry__scope_session_sync(scope); } } diff --git a/src/sentry_core.h b/src/sentry_core.h index 46e546630..8040ef140 100644 --- a/src/sentry_core.h +++ b/src/sentry_core.h @@ -70,8 +70,22 @@ sentry_value_t sentry__ensure_event_id( */ const sentry_options_t *sentry__options_getref(void); +/** + * This will acquire a lock on the global options. + */ +sentry_options_t *sentry__options_lock(void); + +/** + * Release the lock on the global options. + */ +void sentry__options_unlock(void); + #define SENTRY_WITH_OPTIONS(Options) \ for (const sentry_options_t *Options = sentry__options_getref(); Options; \ sentry_options_free((sentry_options_t *)Options), Options = NULL) +#define SENTRY_WITH_OPTIONS_MUT(Options) \ + for (sentry_options_t *Options = sentry__options_lock(); Options; \ + sentry__options_unlock(), Options = NULL) + #endif diff --git a/src/sentry_logger.h b/src/sentry_logger.h index 51fd792f4..f17efbc6b 100644 --- a/src/sentry_logger.h +++ b/src/sentry_logger.h @@ -32,4 +32,6 @@ void sentry__logger_log(sentry_level_t level, const char *message, ...); #define SENTRY_WARN(message) sentry__logger_log(SENTRY_LEVEL_WARNING, message) +#define SENTRY_ERROR(message) sentry__logger_log(SENTRY_LEVEL_ERROR, message) + #endif diff --git a/src/sentry_options.h b/src/sentry_options.h index 09b727522..149a7fb06 100644 --- a/src/sentry_options.h +++ b/src/sentry_options.h @@ -4,6 +4,7 @@ #include "sentry_boot.h" #include "sentry_logger.h" +#include "sentry_session.h" #include "sentry_utils.h" // Defaults to 2s as per @@ -57,6 +58,7 @@ typedef struct sentry_options_s { /* everything from here on down are options which are stored here but not exposed through the options API */ struct sentry_backend_s *backend; + sentry_session_t *session; long user_consent; long refcount; diff --git a/src/sentry_scope.c b/src/sentry_scope.c index 143049e77..5f8992e7e 100644 --- a/src/sentry_scope.c +++ b/src/sentry_scope.c @@ -73,7 +73,6 @@ get_scope(void) g_scope.breadcrumbs = sentry_value_new_list(); g_scope.level = SENTRY_LEVEL_ERROR; g_scope.client_sdk = get_client_sdk(); - g_scope.session = NULL; g_scope_initialized = true; @@ -112,27 +111,16 @@ sentry__scope_unlock(void) } void -sentry__scope_flush_unlock(const sentry_scope_t *scope) +sentry__scope_flush_unlock() { - bool did_unlock = false; + sentry__scope_unlock(); SENTRY_WITH_OPTIONS (options) { - if (scope->session) { - sentry__run_write_session(options->run, scope->session); - sentry__scope_unlock(); - } else { - sentry__scope_unlock(); - sentry__run_clear_session(options->run); - } - did_unlock = true; // we try to unlock the scope/session lock as soon as possible. The // backend will do its own `WITH_SCOPE` internally. if (options->backend && options->backend->flush_scope_func) { options->backend->flush_scope_func(options->backend, options); } } - if (!did_unlock) { - sentry__scope_unlock(); - } } static void @@ -304,24 +292,3 @@ sentry__scope_apply_to_event(const sentry_scope_t *scope, #undef IS_NULL #undef SET } - -void -sentry__scope_session_sync(sentry_scope_t *scope) -{ - if (!scope->session) { - return; - } - - if (!sentry_value_is_null(scope->user)) { - sentry_value_t did = sentry_value_get_by_key(scope->user, "id"); - if (sentry_value_is_null(did)) { - did = sentry_value_get_by_key(scope->user, "email"); - } - if (sentry_value_is_null(did)) { - did = sentry_value_get_by_key(scope->user, "username"); - } - sentry_value_decref(scope->session->distinct_id); - sentry_value_incref(did); - scope->session->distinct_id = did; - } -} diff --git a/src/sentry_scope.h b/src/sentry_scope.h index a6bb6155e..250ff8200 100644 --- a/src/sentry_scope.h +++ b/src/sentry_scope.h @@ -19,7 +19,6 @@ typedef struct sentry_scope_s { sentry_value_t breadcrumbs; sentry_level_t level; sentry_value_t client_sdk; - sentry_session_t *session; } sentry_scope_t; /** @@ -54,11 +53,11 @@ void sentry__scope_unlock(void); void sentry__scope_cleanup(void); /** - * This will notify any backend of scope changes, and persist session - * information to disk. This function must be called while holding the scope - * lock, and it will be unlocked internally. + * This will notify any backend of scope changes. + * This function must be called while holding the scope lock, and it will be + * unlocked internally. */ -void sentry__scope_flush_unlock(const sentry_scope_t *scope); +void sentry__scope_flush_unlock(); /** * This will merge the requested data which is in the given `scope` to the given @@ -70,12 +69,6 @@ void sentry__scope_apply_to_event(const sentry_scope_t *scope, const sentry_options_t *options, sentry_value_t event, sentry_scope_mode_t mode); -/** - * This will update a sessions `distinct_id`, which is generated out of other - * scope data. - */ -void sentry__scope_session_sync(sentry_scope_t *scope); - /** * These are convenience macros to automatically lock/unlock a scope inside a * code block. @@ -85,7 +78,7 @@ void sentry__scope_session_sync(sentry_scope_t *scope); sentry__scope_unlock(), Scope = NULL) #define SENTRY_WITH_SCOPE_MUT(Scope) \ for (sentry_scope_t *Scope = sentry__scope_lock(); Scope; \ - sentry__scope_flush_unlock(Scope), Scope = NULL) + sentry__scope_flush_unlock(), Scope = NULL) #define SENTRY_WITH_SCOPE_MUT_NO_FLUSH(Scope) \ for (sentry_scope_t *Scope = sentry__scope_lock(); Scope; \ sentry__scope_unlock(), Scope = NULL) diff --git a/src/sentry_session.c b/src/sentry_session.c index 38288a309..f58a88e88 100644 --- a/src/sentry_session.c +++ b/src/sentry_session.c @@ -1,5 +1,6 @@ #include "sentry_session.h" #include "sentry_alloc.h" +#include "sentry_database.h" #include "sentry_envelope.h" #include "sentry_json.h" #include "sentry_options.h" @@ -185,9 +186,10 @@ sentry__session_from_json(const char *buf, size_t buflen) sentry_value_get_by_key(value, "errors")); rv->started_ms = sentry__iso8601_to_msec( sentry_value_as_string(sentry_value_get_by_key(value, "started"))); - rv->duration_ms = (uint64_t)( - sentry_value_as_double(sentry_value_get_by_key(value, "duration")) - * 1000); + + double duration + = sentry_value_as_double(sentry_value_get_by_key(value, "duration")); + rv->duration_ms = (uint64_t)(duration * 1000); sentry_value_decref(value); @@ -212,18 +214,23 @@ void sentry_start_session(void) { sentry_end_session(); - SENTRY_WITH_SCOPE_MUT (scope) { - scope->session = sentry__session_new(); - sentry__scope_session_sync(scope); + SENTRY_WITH_SCOPE (scope) { + SENTRY_WITH_OPTIONS_MUT (options) { + options->session = sentry__session_new(); + if (options->session) { + sentry__session_sync_user(options->session, scope->user); + sentry__run_write_session(options->run, options->session); + } + } } } void sentry__record_errors_on_current_session(uint32_t error_count) { - SENTRY_WITH_SCOPE_MUT (scope) { - if (scope->session) { - scope->session->errors += error_count; + SENTRY_WITH_OPTIONS_MUT (options) { + if (options->session) { + options->session->errors += error_count; } } } @@ -232,9 +239,10 @@ static sentry_session_t * sentry__end_session_internal(void) { sentry_session_t *session = NULL; - SENTRY_WITH_SCOPE_MUT (scope) { - session = scope->session; - scope->session = NULL; + SENTRY_WITH_OPTIONS_MUT (options) { + session = options->session; + options->session = NULL; + sentry__run_clear_session(options->run); } if (session && session->status == SENTRY_SESSION_STATUS_OK) { @@ -271,15 +279,17 @@ sentry_end_session(void) } void -sentry__add_current_session_to_envelope(sentry_envelope_t *envelope) +sentry__session_sync_user(sentry_session_t *session, sentry_value_t user) { - SENTRY_WITH_SCOPE_MUT (scope) { - if (scope->session) { - sentry__envelope_add_session(envelope, scope->session); - // we're assuming that if a session is added to an envelope it - // will be sent onwards. This means we now need to set the init - // flag to false because we're no longer the initial session update. - scope->session->init = false; - } + + sentry_value_t did = sentry_value_get_by_key(user, "id"); + if (sentry_value_is_null(did)) { + did = sentry_value_get_by_key(user, "email"); + } + if (sentry_value_is_null(did)) { + did = sentry_value_get_by_key(user, "username"); } + sentry_value_decref(session->distinct_id); + sentry_value_incref(did); + session->distinct_id = did; } diff --git a/src/sentry_session.h b/src/sentry_session.h index 6de5f7ec1..5ba036cbd 100644 --- a/src/sentry_session.h +++ b/src/sentry_session.h @@ -28,7 +28,7 @@ typedef struct sentry_session_s { uint64_t duration_ms; uint64_t errors; sentry_session_status_t status; - bool init; + long init; } sentry_session_t; /** @@ -71,8 +71,8 @@ sentry_session_t *sentry__end_current_session_with_status( void sentry__record_errors_on_current_session(uint32_t error_count); /** - * Add the current session an a new envelope item to `envelope`. + * This will update a sessions `distinct_id`, which is based on the user. */ -void sentry__add_current_session_to_envelope(sentry_envelope_t *envelope); +void sentry__session_sync_user(sentry_session_t *session, sentry_value_t user); #endif diff --git a/src/sentry_sync.c b/src/sentry_sync.c index d81b21628..3b8746beb 100644 --- a/src/sentry_sync.c +++ b/src/sentry_sync.c @@ -207,24 +207,7 @@ sentry__bgworker_is_done(sentry_bgworker_t *bgw) return !bgw->first_task && !sentry__atomic_fetch(&bgw->running); } -#ifdef _MSC_VER -# define THREAD_FUNCTION_API __stdcall -#else -# define THREAD_FUNCTION_API -#endif - -#if defined(__MINGW32__) && !defined(__MINGW64__) -# define UNSIGNED_MINGW unsigned -#else -# define UNSIGNED_MINGW -#endif - -// pthreads use `void *` return types, whereas windows uses `DWORD` -#ifdef SENTRY_PLATFORM_WINDOWS -static UNSIGNED_MINGW DWORD THREAD_FUNCTION_API -#else -static void * -#endif +SENTRY_THREAD_FN worker_thread(void *data) { sentry_bgworker_t *bgw = data; diff --git a/src/sentry_sync.h b/src/sentry_sync.h index d7586c8dd..5c3cbe3ef 100644 --- a/src/sentry_sync.h +++ b/src/sentry_sync.h @@ -7,6 +7,25 @@ #include #include +#ifdef _MSC_VER +# define THREAD_FUNCTION_API __stdcall +#else +# define THREAD_FUNCTION_API +#endif + +#if defined(__MINGW32__) && !defined(__MINGW64__) +# define UNSIGNED_MINGW unsigned +#else +# define UNSIGNED_MINGW +#endif + +// pthreads use `void *` return types, whereas windows uses `DWORD` +#ifdef SENTRY_PLATFORM_WINDOWS +# define SENTRY_THREAD_FN static UNSIGNED_MINGW DWORD THREAD_FUNCTION_API +#else +# define SENTRY_THREAD_FN static void * +#endif + // define a recursive mutex for all platforms #ifdef SENTRY_PLATFORM_WINDOWS # if _WIN32_WINNT >= 0x0600 @@ -200,10 +219,10 @@ typedef CONDITION_VARIABLE sentry_cond_t; we're restricted in what we can do. In particular it's possible that we would end up dead locking ourselves. While we cannot fully prevent races we have a logic here that while the signal handler is active we're - disabling our mutexes so that our signal handler can access what otherwise - would be protected by the mutex but everyone else needs to wait for the - signal handler to finish. This is not without risk because another thread - might still access what the mutex protects. + disabling our mutexes so that our signal handler can access what + otherwise would be protected by the mutex but everyone else needs to wait + for the signal handler to finish. This is not without risk because + another thread might still access what the mutex protects. We are thus taking care that whatever such mutexes protect will not make us crash under concurrent modifications. The mutexes we're likely going diff --git a/tests/cmake.py b/tests/cmake.py index c81c3cc4f..b9b8860bd 100644 --- a/tests/cmake.py +++ b/tests/cmake.py @@ -129,6 +129,8 @@ def cmake(cwd, targets, options=None): configcmd.append("-DSENTRY_BUILD_FORCE32=ON") if "asan" in os.environ.get("RUN_ANALYZER", ""): configcmd.append("-DWITH_ASAN_OPTION=ON") + if "tsan" in os.environ.get("RUN_ANALYZER", ""): + configcmd.append("-DWITH_TSAN_OPTION=ON") # we have to set `-Werror` for this cmake invocation only, otherwise # completely unrelated things will break diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index adecacdfa..91a7e9faf 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -20,6 +20,7 @@ add_executable(sentry_test_unit test_attachments.c test_basic.c test_consent.c + test_concurrency.c test_envelopes.c test_failures.c test_fuzzfailures.c diff --git a/tests/unit/test_concurrency.c b/tests/unit/test_concurrency.c new file mode 100644 index 000000000..e2c1446bf --- /dev/null +++ b/tests/unit/test_concurrency.c @@ -0,0 +1,97 @@ +#include "sentry_core.h" +#include "sentry_testsupport.h" +#include +#include + +static void +send_envelope_test_concurrent(const sentry_envelope_t *envelope, void *data) +{ + sentry__atomic_fetch_and_add((long *)data, 1); + + sentry_value_t event = sentry_envelope_get_event(envelope); + if (!sentry_value_is_null(event)) { + const char *event_id = sentry_value_as_string( + sentry_value_get_by_key(event, "event_id")); + TEST_CHECK_STRING_EQUAL( + event_id, "4c035723-8638-4c3a-923f-2ab9d08b4018"); + } +} + +static void +init_framework(long *called) +{ + sentry_options_t *options = sentry_options_new(); + sentry_options_set_dsn(options, "https://foo@sentry.invalid/42"); + sentry_options_set_transport(options, + sentry_new_function_transport(send_envelope_test_concurrent, called)); + sentry_options_set_release(options, "prod"); + sentry_options_set_require_user_consent(options, false); + sentry_options_set_auto_session_tracking(options, true); + sentry_init(options); +} + +SENTRY_TEST(multiple_inits) +{ + long called = 0; + + init_framework(&called); + init_framework(&called); + + sentry_set_transaction("demo-trans"); + + sentry_capture_event(sentry_value_new_message_event( + SENTRY_LEVEL_INFO, "root", "Hello World!")); + + sentry_value_t obj = sentry_value_new_object(); + // something that is not a uuid, as this will be forcibly changed + sentry_value_set_by_key(obj, "event_id", sentry_value_new_int32(1234)); + sentry_capture_event(obj); + + sentry_close(); + sentry_close(); + + TEST_CHECK_INT_EQUAL(called, 4); +} + +SENTRY_THREAD_FN +thread_worker(void *called) +{ + init_framework(called); + + sentry_set_transaction("demo-trans"); + + sentry_capture_event(sentry_value_new_message_event( + SENTRY_LEVEL_INFO, "root", "Hello World!")); + + sentry_value_t obj = sentry_value_new_object(); + // something that is not a uuid, as this will be forcibly changed + sentry_value_set_by_key(obj, "event_id", sentry_value_new_int32(1234)); + sentry_capture_event(obj); + + return 0; +} + +SENTRY_TEST(concurrent_init) +{ + long called = 0; + +#define THREADS_NUM 10 + sentry_threadid_t threads[THREADS_NUM]; + + for (size_t i = 0; i < THREADS_NUM; i++) { + sentry__thread_init(&threads[i]); + sentry__thread_spawn(&threads[i], &thread_worker, &called); + } + for (size_t i = 0; i < THREADS_NUM; i++) { + sentry__thread_join(threads[i]); + sentry__thread_free(&threads[i]); + } + + sentry_close(); + + // 1 session, and up to 2 events per thread. + // might be less because `capture_event` races with close/init and might + // lose events. + TEST_CHECK(called >= THREADS_NUM * 1); + TEST_CHECK(called <= THREADS_NUM * 3); +} diff --git a/tests/unit/test_session.c b/tests/unit/test_session.c index e79cc266b..5864526f1 100644 --- a/tests/unit/test_session.c +++ b/tests/unit/test_session.c @@ -93,9 +93,12 @@ send_sampled_envelope(const sentry_envelope_t *envelope, void *data) { session_assertion_t *assertion = data; + SENTRY_DEBUG("send_sampled_envelope"); if (assertion->assert_session) { assertion->called += 1; + SENTRY_DEBUG("assertion + 1"); + TEST_CHECK_INT_EQUAL(sentry__envelope_get_item_count(envelope), 1); const sentry_envelope_item_t *item diff --git a/tests/unit/tests.inc b/tests/unit/tests.inc index 08908530a..eb1626160 100644 --- a/tests/unit/tests.inc +++ b/tests/unit/tests.inc @@ -5,6 +5,7 @@ XX(basic_http_request_preparation_for_event) XX(basic_http_request_preparation_for_event_with_attachment) XX(basic_http_request_preparation_for_minidump) XX(buildid_fallback) +XX(concurrent_init) XX(count_sampled_events) XX(custom_logger) XX(dsn_parsing_complete) @@ -22,6 +23,7 @@ XX(module_addr) XX(module_finder) XX(mpack_newlines) XX(mpack_removed_tags) +XX(multiple_inits) XX(os) XX(page_allocator) XX(path_basics) From 8b9a43da6f57d1e6d8f6ed2ced25c33f95624d90 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Wed, 7 Jul 2021 11:34:57 +0200 Subject: [PATCH 36/49] meta: Draft Changelog (#572) --- CHANGELOG.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bbfc80f82..8efaeb8f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## Unreleased + +**Fixes**: + +- The crashpad backend now respects the `max_breadcrumbs` setting. +- Hanging HTTP requests will now be canceled on shutdown in the WinHTTP transport. +- The Modulefinder and Android unwinder now use safer memory access. +- Possible races and deadlocks have been fixed in `init`/`close`, and in API related to sessions. + +**Thank you**: + +Features, fixes and improvements in this release have been contributed by: + +- [@smibe](https://github.com/smibe) + ## 0.4.10 **Fixes**: From 5afac54be8c8956b58c9f5cefd0828948aa3563f Mon Sep 17 00:00:00 2001 From: getsentry-bot Date: Wed, 7 Jul 2021 09:40:30 +0000 Subject: [PATCH 37/49] release: 0.4.11 --- CHANGELOG.md | 2 +- include/sentry.h | 2 +- tests/assertions.py | 4 ++-- tests/test_integration_http.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8efaeb8f6..867231b75 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## Unreleased +## 0.4.11 **Fixes**: diff --git a/include/sentry.h b/include/sentry.h index 2fbe7b279..8defce2be 100644 --- a/include/sentry.h +++ b/include/sentry.h @@ -24,7 +24,7 @@ extern "C" { /* SDK Version */ #define SENTRY_SDK_NAME "sentry.native" -#define SENTRY_SDK_VERSION "0.4.10" +#define SENTRY_SDK_VERSION "0.4.11" #define SENTRY_SDK_USER_AGENT SENTRY_SDK_NAME "/" SENTRY_SDK_VERSION /* common platform detection */ diff --git a/tests/assertions.py b/tests/assertions.py index 0f4634562..db04a7038 100644 --- a/tests/assertions.py +++ b/tests/assertions.py @@ -44,8 +44,8 @@ def assert_meta(envelope, release="test-example-release", integration=None): } expected_sdk = { "name": "sentry.native", - "version": "0.4.10", - "packages": [{"name": "github:getsentry/sentry-native", "version": "0.4.10"},], + "version": "0.4.11", + "packages": [{"name": "github:getsentry/sentry-native", "version": "0.4.11"},], } if not is_android: if sys.platform == "win32": diff --git a/tests/test_integration_http.py b/tests/test_integration_http.py index 9f4827c6a..65a033b2d 100644 --- a/tests/test_integration_http.py +++ b/tests/test_integration_http.py @@ -21,7 +21,7 @@ pytestmark = pytest.mark.skipif(not has_http, reason="tests need http") -auth_header = "Sentry sentry_key=uiaeosnrtdy, sentry_version=7, sentry_client=sentry.native/0.4.10" +auth_header = "Sentry sentry_key=uiaeosnrtdy, sentry_version=7, sentry_client=sentry.native/0.4.11" def test_capture_http(cmake, httpserver): From 290fa46ec868b5f23f720fff253c1da8746ce249 Mon Sep 17 00:00:00 2001 From: andrei-mu Date: Thu, 15 Jul 2021 21:04:38 +0200 Subject: [PATCH 38/49] feat: Make shutdown timeout customizable (#577) Co-authored-by: Andrei Muraru --- include/sentry.h | 13 +++++++++++++ src/sentry_core.c | 3 +-- src/sentry_options.c | 14 ++++++++++++++ src/sentry_options.h | 1 + 4 files changed, 29 insertions(+), 2 deletions(-) diff --git a/include/sentry.h b/include/sentry.h index 8defce2be..bc9143739 100644 --- a/include/sentry.h +++ b/include/sentry.h @@ -1006,6 +1006,19 @@ SENTRY_API void sentry_options_set_database_pathw( SENTRY_API void sentry_options_set_system_crash_reporter_enabled( sentry_options_t *opts, int enabled); +/** + * Sets the maximum time (in milliseconds) to wait for the asynchronous tasks to + * end on shutdown, before attempting a forced termination. + */ +SENTRY_API void sentry_options_set_shutdown_timeout( + sentry_options_t *opts, uint64_t shutdown_timeout); + +/** + * Gets the maximum time (in milliseconds) to wait for the asynchronous tasks to + * end on shutdown, before attempting a forced termination. + */ +SENTRY_API uint64_t sentry_options_get_shutdown_timeout(sentry_options_t *opts); + /* -- Global APIs -- */ /** diff --git a/src/sentry_core.c b/src/sentry_core.c index 56f8e66b2..2904ce7c2 100644 --- a/src/sentry_core.c +++ b/src/sentry_core.c @@ -213,9 +213,8 @@ sentry_close(void) } if (options->transport) { - // TODO: make this configurable if (sentry__transport_shutdown( - options->transport, SENTRY_DEFAULT_SHUTDOWN_TIMEOUT) + options->transport, options->shutdown_timeout) != 0) { SENTRY_WARN("transport did not shut down cleanly"); } diff --git a/src/sentry_options.c b/src/sentry_options.c index 8468c2c58..82445ad61 100644 --- a/src/sentry_options.c +++ b/src/sentry_options.c @@ -47,6 +47,7 @@ sentry_options_new(void) opts->transport = sentry__transport_new_default(); opts->sample_rate = 1.0; opts->refcount = 1; + opts->shutdown_timeout = SENTRY_DEFAULT_SHUTDOWN_TIMEOUT; return opts; } @@ -297,6 +298,19 @@ sentry_options_set_system_crash_reporter_enabled( opts->system_crash_reporter_enabled = !!enabled; } +void +sentry_options_set_shutdown_timeout( + sentry_options_t *opts, uint64_t shutdown_timeout) +{ + opts->shutdown_timeout = shutdown_timeout; +} + +uint64_t +sentry_options_get_shutdown_timeout(sentry_options_t *opts) +{ + return opts->shutdown_timeout; +} + static void add_attachment(sentry_options_t *opts, sentry_path_t *path) { diff --git a/src/sentry_options.h b/src/sentry_options.h index 149a7fb06..c9eab8120 100644 --- a/src/sentry_options.h +++ b/src/sentry_options.h @@ -62,6 +62,7 @@ typedef struct sentry_options_s { long user_consent; long refcount; + uint64_t shutdown_timeout; } sentry_options_t; /** From c882275f69336e8c41606a9fc5a8e4044ed7a728 Mon Sep 17 00:00:00 2001 From: pastdue <30942300+past-due@users.noreply.github.com> Date: Tue, 20 Jul 2021 08:44:00 -0400 Subject: [PATCH 39/49] CMake: Link to the CURL::libcurl target when available (#579) Caters better for newer cmake versions. --- CMakeLists.txt | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 696d270ae..a893ccf78 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -250,13 +250,17 @@ target_compile_definitions(sentry PRIVATE SIZEOF_LONG=${CMAKE_SIZEOF_LONG}) if(SENTRY_TRANSPORT_CURL) find_package(CURL REQUIRED) - target_include_directories(sentry PRIVATE ${CURL_INCLUDE_DIR}) - # The exported sentry target must not contain any path of the build machine, therefore use generator expressions - # FIXME: cmake 3.12 introduced the target CURL::libcurl - string(REPLACE ";" "$" GENEX_CURL_LIBRARIES "${CURL_LIBRARIES}") - string(REPLACE ";" "$" GENEX_CURL_COMPILE_DEFINITIONS "${CURL_COMPILE_DEFINITIONS}") - target_link_libraries(sentry PRIVATE $) - target_compile_definitions(sentry PRIVATE $) + if(TARGET CURL::libcurl) # Only available in cmake 3.12+ + target_link_libraries(sentry PRIVATE CURL::libcurl) + else() + # Needed for cmake < 3.12 support (cmake 3.12 introduced the target CURL::libcurl) + target_include_directories(sentry PRIVATE ${CURL_INCLUDE_DIR}) + # The exported sentry target must not contain any path of the build machine, therefore use generator expressions + string(REPLACE ";" "$" GENEX_CURL_LIBRARIES "${CURL_LIBRARIES}") + string(REPLACE ";" "$" GENEX_CURL_COMPILE_DEFINITIONS "${CURL_COMPILE_DEFINITIONS}") + target_link_libraries(sentry PRIVATE $) + target_compile_definitions(sentry PRIVATE $) + endif() endif() set_property(TARGET sentry PROPERTY C_VISIBILITY_PRESET hidden) From c3d490fc35c4265db11376700b0d43300b10a0e8 Mon Sep 17 00:00:00 2001 From: pastdue <30942300+past-due@users.noreply.github.com> Date: Tue, 20 Jul 2021 12:04:24 -0400 Subject: [PATCH 40/49] meta: Update crashpad to 2021-07-14 (#580) --- CHANGELOG.md | 14 ++++++++++++++ external/crashpad | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 867231b75..f6d2406f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## Unreleased + +**Fixes**: + +- The crashpad backend compiles with mingw again. +- Build System improvements. + +**Thank you**: + +Features, fixes and improvements in this release have been contributed by: + +- [@irov](https://github.com/irov) +- [@past-due](https://github.com/past-due) + ## 0.4.11 **Fixes**: diff --git a/external/crashpad b/external/crashpad index 5cf3032b2..7a31c8491 160000 --- a/external/crashpad +++ b/external/crashpad @@ -1 +1 @@ -Subproject commit 5cf3032b2281cf0928acc8bccf69f91ccf26b939 +Subproject commit 7a31c8491ec771d7f7577c63585e2c4c4d49c03c From 46a16b4c5d8a17195acd231e00f43ba39d8236e3 Mon Sep 17 00:00:00 2001 From: Roshan Padaki Date: Fri, 30 Jul 2021 03:25:12 -0400 Subject: [PATCH 41/49] fix: Properly use `SENTRY_BUILD_RUNTIMESTATIC` for `sentry_fuzz_json` unit test (#583) --- CMakeLists.txt | 2 +- tests/unit/CMakeLists.txt | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a893ccf78..0bc454c78 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -410,7 +410,7 @@ if(SENTRY_BACKEND_CRASHPAD) endif() add_subdirectory(external/crashpad crashpad_build) - # set static runime if enabled + # set static runtime if enabled if(SENTRY_BUILD_RUNTIMESTATIC AND MSVC) set_property(TARGET crashpad_client PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") set_property(TARGET crashpad_compat PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 91a7e9faf..86ffecaff 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -103,4 +103,9 @@ if(MSVC) target_compile_options(sentry_fuzz_json PRIVATE $) endif() +# set static runtime if enabled +if (SENTRY_BUILD_RUNTIMESTATIC AND MSVC) + set_property(TARGET sentry_fuzz_json PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") +endif() + add_test(NAME sentry_fuzz_json COMMAND sentry_fuzz_json) From f90f59460635683441e045e7b4443d6cf8416bac Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Tue, 3 Aug 2021 15:54:12 +0200 Subject: [PATCH 42/49] meta: Update break/crashpad to 2021-07-28 (#584) --- CHANGELOG.md | 6 ++++++ external/breakpad | 2 +- external/crashpad | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f6d2406f7..61bc2a826 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +**Features**: + +- Make the shutdown timeout configurable via `sentry_options_set_shutdown_timeout`. + **Fixes**: - The crashpad backend compiles with mingw again. @@ -13,6 +17,8 @@ Features, fixes and improvements in this release have been contributed by: - [@irov](https://github.com/irov) - [@past-due](https://github.com/past-due) +- [@andrei-mu](https://github.com/andrei-mu) +- [@rpadaki](https://github.com/rpadaki) ## 0.4.11 diff --git a/external/breakpad b/external/breakpad index ba407b2ff..1f58de28f 160000 --- a/external/breakpad +++ b/external/breakpad @@ -1 +1 @@ -Subproject commit ba407b2ff475e93a8d325ae20e8777befd0c5c43 +Subproject commit 1f58de28fa974eac347e8bf3d998ba4465248cc8 diff --git a/external/crashpad b/external/crashpad index 7a31c8491..e1e78cd7f 160000 --- a/external/crashpad +++ b/external/crashpad @@ -1 +1 @@ -Subproject commit 7a31c8491ec771d7f7577c63585e2c4c4d49c03c +Subproject commit e1e78cd7facbf554c6bbfb5dbbf58e864017bb99 From 3436a29d839aa7437548be940ab62a85ca699635 Mon Sep 17 00:00:00 2001 From: getsentry-bot Date: Tue, 3 Aug 2021 13:55:29 +0000 Subject: [PATCH 43/49] release: 0.4.12 --- CHANGELOG.md | 2 +- include/sentry.h | 2 +- tests/assertions.py | 4 ++-- tests/test_integration_http.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 61bc2a826..eba5d6f3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## Unreleased +## 0.4.12 **Features**: diff --git a/include/sentry.h b/include/sentry.h index bc9143739..2d71bf4da 100644 --- a/include/sentry.h +++ b/include/sentry.h @@ -24,7 +24,7 @@ extern "C" { /* SDK Version */ #define SENTRY_SDK_NAME "sentry.native" -#define SENTRY_SDK_VERSION "0.4.11" +#define SENTRY_SDK_VERSION "0.4.12" #define SENTRY_SDK_USER_AGENT SENTRY_SDK_NAME "/" SENTRY_SDK_VERSION /* common platform detection */ diff --git a/tests/assertions.py b/tests/assertions.py index db04a7038..efb05c39b 100644 --- a/tests/assertions.py +++ b/tests/assertions.py @@ -44,8 +44,8 @@ def assert_meta(envelope, release="test-example-release", integration=None): } expected_sdk = { "name": "sentry.native", - "version": "0.4.11", - "packages": [{"name": "github:getsentry/sentry-native", "version": "0.4.11"},], + "version": "0.4.12", + "packages": [{"name": "github:getsentry/sentry-native", "version": "0.4.12"},], } if not is_android: if sys.platform == "win32": diff --git a/tests/test_integration_http.py b/tests/test_integration_http.py index 65a033b2d..502d3bd45 100644 --- a/tests/test_integration_http.py +++ b/tests/test_integration_http.py @@ -21,7 +21,7 @@ pytestmark = pytest.mark.skipif(not has_http, reason="tests need http") -auth_header = "Sentry sentry_key=uiaeosnrtdy, sentry_version=7, sentry_client=sentry.native/0.4.11" +auth_header = "Sentry sentry_key=uiaeosnrtdy, sentry_version=7, sentry_client=sentry.native/0.4.12" def test_capture_http(cmake, httpserver): From 90966cc1022b8155681b6899539b35466baccf2c Mon Sep 17 00:00:00 2001 From: mjvankampen Date: Tue, 24 Aug 2021 23:24:21 +0200 Subject: [PATCH 44/49] fix: Increment CXX standard to 14 to allow crashpad build (#585) Fixes #574 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0bc454c78..6b03afdba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,7 +30,7 @@ if(NOT CMAKE_C_STANDARD) endif() if(NOT CMAKE_CXX_STANDARD) - set(CMAKE_CXX_STANDARD 11) + set(CMAKE_CXX_STANDARD 14) endif() include(GNUInstallDirs) From 2102b30813210c5586177feebd62f45e5365c390 Mon Sep 17 00:00:00 2001 From: Yucel Albar Date: Fri, 15 Oct 2021 09:42:00 +0300 Subject: [PATCH 45/49] feat(STAFF-121291): Edits to be compatible with Windows --- src/backends/sentry_backend_crashpad.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/backends/sentry_backend_crashpad.cpp b/src/backends/sentry_backend_crashpad.cpp index 6ec8b3ab6..31f2e325e 100644 --- a/src/backends/sentry_backend_crashpad.cpp +++ b/src/backends/sentry_backend_crashpad.cpp @@ -211,8 +211,17 @@ sentry__crashpad_backend_startup( sentry_path_t *current_exe = sentry__path_current_exe(); if (current_exe && options->relaunch_argv) { annotations["__td-crashed-pid"] = std::to_string(td__getpid()); - annotations["__td-relaunch-path"] = std::string(current_exe->path); annotations["__td-relaunch-argv"] = std::string(options->relaunch_argv); +#ifdef SENTRY_PLATFORM_WINDOWS + std::wstring wstrPath(current_exe->path); + std::string strPath; + std::transform(wstrPath.begin(), wstrPath.end(), + std::back_inserter(strPath), [](wchar_t c) { return (char)c; }); + + annotations["__td-relaunch-path"] = strPath; +#else + annotations["__td-relaunch-path"] = std::string(current_exe->path); +#endif sentry__path_free(current_exe); } From 04c7a766d70aa6fc940b8971e24b0f2d1f4e2a22 Mon Sep 17 00:00:00 2001 From: Floris Bruynooghe Date: Fri, 15 Oct 2021 12:26:11 +0200 Subject: [PATCH 46/49] meta: Bump python dependencies (#600) The old version of pytest breaks with python 3.10 which changed a little how code object internals work. Since python 3.10 is now released it starts being used in CI. --- tests/__init__.py | 13 ++++- tests/assertions.py | 4 +- tests/cmake.py | 8 ++- tests/requirements.txt | 6 +- tests/test_integration_crashpad.py | 5 +- tests/test_integration_http.py | 94 +++++++++++++++++++++++------- tests/test_integration_stdout.py | 15 +++-- 7 files changed, 111 insertions(+), 34 deletions(-) diff --git a/tests/__init__.py b/tests/__init__.py index a995441a0..02f614664 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -20,7 +20,13 @@ def make_dsn(httpserver, auth="uiaeosnrtdy", id=123456): # after a timeout of 2 seconds, falling back to the ipv4 loopback instead. host = url.netloc.replace("localhost", "127.0.0.1") return urllib.parse.urlunsplit( - (url.scheme, "{}@{}".format(auth, host), url.path, url.query, url.fragment,) + ( + url.scheme, + "{}@{}".format(auth, host), + url.path, + url.query, + url.fragment, + ) ) @@ -230,4 +236,7 @@ def deserialize( def __repr__(self): # type: (...) -> str - return "" % (self.headers, self.payload,) + return "" % ( + self.headers, + self.payload, + ) diff --git a/tests/assertions.py b/tests/assertions.py index efb05c39b..2be06c18f 100644 --- a/tests/assertions.py +++ b/tests/assertions.py @@ -45,7 +45,9 @@ def assert_meta(envelope, release="test-example-release", integration=None): expected_sdk = { "name": "sentry.native", "version": "0.4.12", - "packages": [{"name": "github:getsentry/sentry-native", "version": "0.4.12"},], + "packages": [ + {"name": "github:getsentry/sentry-native", "version": "0.4.12"}, + ], } if not is_android: if sys.platform == "win32": diff --git a/tests/cmake.py b/tests/cmake.py index b9b8860bd..c59ba6db8 100644 --- a/tests/cmake.py +++ b/tests/cmake.py @@ -76,7 +76,13 @@ def destroy(self): ] if len(coverage_dirs) > 0: subprocess.run( - ["kcov", "--clean", "--merge", coveragedir, *coverage_dirs,] + [ + "kcov", + "--clean", + "--merge", + coveragedir, + *coverage_dirs, + ] ) diff --git a/tests/requirements.txt b/tests/requirements.txt index 440b71546..fcbf849f6 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -1,3 +1,3 @@ -black==19.10b0 -pytest==5.4.1 -pytest-httpserver==1.0.0 +black==21.9b0 +pytest==6.2.5 +pytest-httpserver==1.0.1 diff --git a/tests/test_integration_crashpad.py b/tests/test_integration_crashpad.py index ba56b6ee2..7eb0ca2c2 100644 --- a/tests/test_integration_crashpad.py +++ b/tests/test_integration_crashpad.py @@ -95,7 +95,10 @@ def test_crashpad_crash_after_shutdown(cmake, httpserver): with httpserver.wait(timeout=10) as waiting: child = run( - tmp_path, "sentry_example", ["log", "crash-after-shutdown"], env=env, + tmp_path, + "sentry_example", + ["log", "crash-after-shutdown"], + env=env, ) assert child.returncode # well, its a crash after all diff --git a/tests/test_integration_http.py b/tests/test_integration_http.py index 502d3bd45..ae640ec86 100644 --- a/tests/test_integration_http.py +++ b/tests/test_integration_http.py @@ -28,7 +28,8 @@ def test_capture_http(cmake, httpserver): tmp_path = cmake(["sentry_example"], {"SENTRY_BACKEND": "none"}) httpserver.expect_oneshot_request( - "/api/123456/envelope/", headers={"x-sentry-auth": auth_header}, + "/api/123456/envelope/", + headers={"x-sentry-auth": auth_header}, ).respond_with_data("OK") env = dict(os.environ, SENTRY_DSN=make_dsn(httpserver), SENTRY_RELEASE="🤮🚀") @@ -55,7 +56,8 @@ def test_session_http(cmake, httpserver): tmp_path = cmake(["sentry_example"], {"SENTRY_BACKEND": "none"}) httpserver.expect_request( - "/api/123456/envelope/", headers={"x-sentry-auth": auth_header}, + "/api/123456/envelope/", + headers={"x-sentry-auth": auth_header}, ).respond_with_data("OK") env = dict(os.environ, SENTRY_DSN=make_dsn(httpserver)) @@ -68,7 +70,11 @@ def test_session_http(cmake, httpserver): env=env, ) run( - tmp_path, "sentry_example", ["log", "start-session"], check=True, env=env, + tmp_path, + "sentry_example", + ["log", "start-session"], + check=True, + env=env, ) assert len(httpserver.log) == 1 @@ -82,7 +88,8 @@ def test_capture_and_session_http(cmake, httpserver): tmp_path = cmake(["sentry_example"], {"SENTRY_BACKEND": "none"}) httpserver.expect_request( - "/api/123456/envelope/", headers={"x-sentry-auth": auth_header}, + "/api/123456/envelope/", + headers={"x-sentry-auth": auth_header}, ).respond_with_data("OK") env = dict(os.environ, SENTRY_DSN=make_dsn(httpserver)) @@ -110,7 +117,8 @@ def test_exception_and_session_http(cmake, httpserver): tmp_path = cmake(["sentry_example"], {"SENTRY_BACKEND": "none"}) httpserver.expect_request( - "/api/123456/envelope/", headers={"x-sentry-auth": auth_header}, + "/api/123456/envelope/", + headers={"x-sentry-auth": auth_header}, ).respond_with_data("OK") env = dict(os.environ, SENTRY_DSN=make_dsn(httpserver)) @@ -137,10 +145,14 @@ def test_exception_and_session_http(cmake, httpserver): @pytest.mark.skipif(not has_files, reason="test needs a local filesystem") def test_abnormal_session(cmake, httpserver): - tmp_path = cmake(["sentry_example"], {"SENTRY_BACKEND": "none"},) + tmp_path = cmake( + ["sentry_example"], + {"SENTRY_BACKEND": "none"}, + ) httpserver.expect_request( - "/api/123456/envelope/", headers={"x-sentry-auth": auth_header}, + "/api/123456/envelope/", + headers={"x-sentry-auth": auth_header}, ).respond_with_data("OK") env = dict(os.environ, SENTRY_DSN=make_dsn(httpserver)) @@ -166,7 +178,11 @@ def test_abnormal_session(cmake, httpserver): session_file.write(session) run( - tmp_path, "sentry_example", ["log", "no-setup"], check=True, env=env, + tmp_path, + "sentry_example", + ["log", "no-setup"], + check=True, + env=env, ) assert len(httpserver.log) == 2 @@ -186,7 +202,8 @@ def test_inproc_crash_http(cmake, httpserver): tmp_path = cmake(["sentry_example"], {"SENTRY_BACKEND": "inproc"}) httpserver.expect_request( - "/api/123456/envelope/", headers={"x-sentry-auth": auth_header}, + "/api/123456/envelope/", + headers={"x-sentry-auth": auth_header}, ).respond_with_data("OK") env = dict(os.environ, SENTRY_DSN=make_dsn(httpserver)) @@ -199,7 +216,11 @@ def test_inproc_crash_http(cmake, httpserver): assert child.returncode # well, its a crash after all run( - tmp_path, "sentry_example", ["log", "no-setup"], check=True, env=env, + tmp_path, + "sentry_example", + ["log", "no-setup"], + check=True, + env=env, ) assert len(httpserver.log) == 1 @@ -219,14 +240,24 @@ def test_inproc_reinstall(cmake, httpserver): env = dict(os.environ, SENTRY_DSN=make_dsn(httpserver)) httpserver.expect_request( - "/api/123456/envelope/", headers={"x-sentry-auth": auth_header}, + "/api/123456/envelope/", + headers={"x-sentry-auth": auth_header}, ).respond_with_data("OK") - child = run(tmp_path, "sentry_example", ["log", "reinstall", "crash"], env=env,) + child = run( + tmp_path, + "sentry_example", + ["log", "reinstall", "crash"], + env=env, + ) assert child.returncode # well, its a crash after all run( - tmp_path, "sentry_example", ["log", "no-setup"], check=True, env=env, + tmp_path, + "sentry_example", + ["log", "no-setup"], + check=True, + env=env, ) assert len(httpserver.log) == 1 @@ -236,7 +267,8 @@ def test_inproc_dump_inflight(cmake, httpserver): tmp_path = cmake(["sentry_example"], {"SENTRY_BACKEND": "inproc"}) httpserver.expect_request( - "/api/123456/envelope/", headers={"x-sentry-auth": auth_header}, + "/api/123456/envelope/", + headers={"x-sentry-auth": auth_header}, ).respond_with_data("OK") env = dict(os.environ, SENTRY_DSN=make_dsn(httpserver)) @@ -256,7 +288,8 @@ def test_breakpad_crash_http(cmake, httpserver): tmp_path = cmake(["sentry_example"], {"SENTRY_BACKEND": "breakpad"}) httpserver.expect_request( - "/api/123456/envelope/", headers={"x-sentry-auth": auth_header}, + "/api/123456/envelope/", + headers={"x-sentry-auth": auth_header}, ).respond_with_data("OK") env = dict(os.environ, SENTRY_DSN=make_dsn(httpserver)) @@ -269,7 +302,11 @@ def test_breakpad_crash_http(cmake, httpserver): assert child.returncode # well, its a crash after all run( - tmp_path, "sentry_example", ["log", "no-setup"], check=True, env=env, + tmp_path, + "sentry_example", + ["log", "no-setup"], + check=True, + env=env, ) assert len(httpserver.log) == 1 @@ -290,14 +327,24 @@ def test_breakpad_reinstall(cmake, httpserver): env = dict(os.environ, SENTRY_DSN=make_dsn(httpserver)) httpserver.expect_request( - "/api/123456/envelope/", headers={"x-sentry-auth": auth_header}, + "/api/123456/envelope/", + headers={"x-sentry-auth": auth_header}, ).respond_with_data("OK") - child = run(tmp_path, "sentry_example", ["log", "reinstall", "crash"], env=env,) + child = run( + tmp_path, + "sentry_example", + ["log", "reinstall", "crash"], + env=env, + ) assert child.returncode # well, its a crash after all run( - tmp_path, "sentry_example", ["log", "no-setup"], check=True, env=env, + tmp_path, + "sentry_example", + ["log", "no-setup"], + check=True, + env=env, ) assert len(httpserver.log) == 1 @@ -308,7 +355,8 @@ def test_breakpad_dump_inflight(cmake, httpserver): tmp_path = cmake(["sentry_example"], {"SENTRY_BACKEND": "breakpad"}) httpserver.expect_request( - "/api/123456/envelope/", headers={"x-sentry-auth": auth_header}, + "/api/123456/envelope/", + headers={"x-sentry-auth": auth_header}, ).respond_with_data("OK") env = dict(os.environ, SENTRY_DSN=make_dsn(httpserver)) @@ -339,7 +387,8 @@ def delayed(req): return "{}" httpserver.expect_request( - "/api/123456/envelope/", headers={"x-sentry-auth": auth_header}, + "/api/123456/envelope/", + headers={"x-sentry-auth": auth_header}, ).respond_with_handler(delayed) env = dict(os.environ, SENTRY_DSN=make_dsn(httpserver)) @@ -357,7 +406,8 @@ def delayed(req): httpserver.clear_log() httpserver.expect_request( - "/api/123456/envelope/", headers={"x-sentry-auth": auth_header}, + "/api/123456/envelope/", + headers={"x-sentry-auth": auth_header}, ).respond_with_data("OK") run(tmp_path, "sentry_example", ["log", "no-setup"], check=True, env=env) diff --git a/tests/test_integration_stdout.py b/tests/test_integration_stdout.py index 708e18f90..8998fefba 100644 --- a/tests/test_integration_stdout.py +++ b/tests/test_integration_stdout.py @@ -20,7 +20,11 @@ def test_capture_stdout(cmake): tmp_path = cmake( - ["sentry_example"], {"SENTRY_BACKEND": "none", "SENTRY_TRANSPORT": "none",}, + ["sentry_example"], + { + "SENTRY_BACKEND": "none", + "SENTRY_TRANSPORT": "none", + }, ) output = check_output( @@ -43,7 +47,8 @@ def test_multi_process(cmake): # NOTE: It would have been nice to do *everything* in a unicode-named # directory, but apparently cmake does not like that either. tmp_path = cmake( - ["sentry_example"], {"SENTRY_BACKEND": "none", "SENTRY_TRANSPORT": "none"}, + ["sentry_example"], + {"SENTRY_BACKEND": "none", "SENTRY_TRANSPORT": "none"}, ) cwd = tmp_path.joinpath("unicode ❤️ Юля") @@ -86,7 +91,8 @@ def test_multi_process(cmake): def test_inproc_crash_stdout(cmake): tmp_path = cmake( - ["sentry_example"], {"SENTRY_BACKEND": "inproc", "SENTRY_TRANSPORT": "none"}, + ["sentry_example"], + {"SENTRY_BACKEND": "inproc", "SENTRY_TRANSPORT": "none"}, ) child = run(tmp_path, "sentry_example", ["attachment", "crash"]) @@ -112,7 +118,8 @@ def test_inproc_crash_stdout(cmake): @pytest.mark.skipif(not has_breakpad, reason="test needs breakpad backend") def test_breakpad_crash_stdout(cmake): tmp_path = cmake( - ["sentry_example"], {"SENTRY_BACKEND": "breakpad", "SENTRY_TRANSPORT": "none"}, + ["sentry_example"], + {"SENTRY_BACKEND": "breakpad", "SENTRY_TRANSPORT": "none"}, ) child = run(tmp_path, "sentry_example", ["attachment", "crash"]) From ebc6f48ff1a1d1530c08e42c4ddca35df673acf1 Mon Sep 17 00:00:00 2001 From: Yucel Albar Date: Fri, 15 Oct 2021 14:33:40 +0300 Subject: [PATCH 47/49] Skip changes on ci.yml file --- .github/workflows/ci.yml | 42 +--------------------------------------- 1 file changed, 1 insertion(+), 41 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c9eb76fa8..d9bb1abf2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -64,59 +64,19 @@ jobs: os: ubuntu-20.04 CC: gcc-10 CXX: g++-10 - # ERROR_ON_WARNINGS: 1 - # The GCC analyzer 10.0.1 (as on CI) has an internal compiler error - # currently, and is not stable enough to activate. - # RUN_ANALYZER: gcc - - name: Linux (clang 11 + asan + llvm-cov) - os: ubuntu-20.04 - CC: clang-11 - CXX: clang++-11 - ERROR_ON_WARNINGS: 1 - RUN_ANALYZER: asan,llvm-cov - - name: Linux (clang 11 + kcov) - os: ubuntu-20.04 - CC: clang-11 - CXX: clang++-11 - ERROR_ON_WARNINGS: 1 - RUN_ANALYZER: kcov - - name: Linux (code-checker + valgrind) - os: ubuntu-20.04 - RUN_ANALYZER: code-checker,valgrind - - name: macOS (xcode llvm) - os: macOs-latest - ERROR_ON_WARNINGS: 1 - - name: macOS (xcode llvm + universal) - os: macOs-latest - ERROR_ON_WARNINGS: 1 - CMAKE_DEFINES: -DCMAKE_OSX_ARCHITECTURES=arm64;x86_64 - - name: macOS (clang 11 + asan + llvm-cov) + - name: macOS (xcode) os: macOs-latest ERROR_ON_WARNINGS: 1 - name: Windows (VS2017, 32bit) os: vs2017-win2016 - name: Windows (latest) os: windows-latest - # The Android emulator is currently only available on macos, see: - # https://docs.microsoft.com/en-us/azure/devops/pipelines/ecosystems/android?view=azure-devops#test-on-the-android-emulator - - name: Android (API 16, NDK 20) - os: macOs-latest - ANDROID_API: 16 - ANDROID_NDK: 20.1.5948944 - - name: Android (API 30, NDK 22) - os: macOs-latest - ANDROID_API: 30 - ANDROID_NDK: 22.1.7171670 name: ${{ matrix.name }} runs-on: ${{ matrix.os }} env: ERROR_ON_WARNINGS: ${{ matrix.ERROR_ON_WARNINGS }} - RUN_ANALYZER: ${{ matrix.RUN_ANALYZER }} - ANDROID_API: ${{ matrix.ANDROID_API }} - ANDROID_NDK: ${{ matrix.ANDROID_NDK }} - CMAKE_DEFINES: ${{ matrix.CMAKE_DEFINES }} steps: - uses: actions/checkout@v2 From 812f06cc5b1c5e3fe6b42370f4d908866c84c835 Mon Sep 17 00:00:00 2001 From: Yucel Albar Date: Fri, 15 Oct 2021 15:52:03 +0300 Subject: [PATCH 48/49] Change xcode version to 13.0.0 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d9bb1abf2..8de88c126 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -120,7 +120,7 @@ jobs: if: ${{ runner.os == 'macOS' }} uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: '11.3.1' + xcode-version: '13.0.0' - name: Building (macOS) if: ${{ runner.os == 'macOS' }} From 347918722000302c3776969c89c59d5b7bc57b2e Mon Sep 17 00:00:00 2001 From: yucelalbar Date: Wed, 20 Oct 2021 14:47:49 +0300 Subject: [PATCH 49/49] git submodule updated --- external/breakpad | 2 +- external/crashpad | 2 +- external/libunwindstack-ndk | 2 +- external/third_party/lss | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/external/breakpad b/external/breakpad index 1f58de28f..6690e19d9 160000 --- a/external/breakpad +++ b/external/breakpad @@ -1 +1 @@ -Subproject commit 1f58de28fa974eac347e8bf3d998ba4465248cc8 +Subproject commit 6690e19d9d42874a782e154f2fc7fd55c7a9dc91 diff --git a/external/crashpad b/external/crashpad index d11e19648..6415106e7 160000 --- a/external/crashpad +++ b/external/crashpad @@ -1 +1 @@ -Subproject commit d11e196480ef5ab4a521e7996fe855626dee120c +Subproject commit 6415106e7997c6c1886f17d0734ac6d749fc1a99 diff --git a/external/libunwindstack-ndk b/external/libunwindstack-ndk index abcfef9e7..a4c27d48d 160000 --- a/external/libunwindstack-ndk +++ b/external/libunwindstack-ndk @@ -1 +1 @@ -Subproject commit abcfef9e77ec6c8b0fe179591e354b8509727686 +Subproject commit a4c27d48deff95fe922fe9733ef5c1339bdbf4fb diff --git a/external/third_party/lss b/external/third_party/lss index 29f7c7e01..e1e7b0ad8 160000 --- a/external/third_party/lss +++ b/external/third_party/lss @@ -1 +1 @@ -Subproject commit 29f7c7e018f4ce706a709f0b0afbf8bacf869480 +Subproject commit e1e7b0ad8ee99a875b272c8e33e308472e897660