-
-
Notifications
You must be signed in to change notification settings - Fork 334
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: Cache binary images to be used for crashes (#2939)
During crashes we use 2 async-signal-unsafe functions _dyld_get_image_header and _dyld_get_image_name. This may lead to a crash report not being properly saved to disk. This proposal will cache binary images during runtime and use it when the app crashes. Co-authored-by: Andrew McKnight <andrew.mcknight@sentry.io> Co-authored-by: Philipp Hofmann <philipp.hofmann@sentry.io>
- Loading branch information
1 parent
79a9b4f
commit 6943de0
Showing
15 changed files
with
591 additions
and
33 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
180 changes: 180 additions & 0 deletions
180
Sources/SentryCrash/Recording/SentryCrashBinaryImageCache.c
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
#include "SentryCrashBinaryImageCache.h" | ||
#include "SentryCrashDynamicLinker.h" | ||
#include <mach-o/dyld.h> | ||
#include <pthread.h> | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include <string.h> | ||
#include <unistd.h> | ||
|
||
#if TEST || TESTCI | ||
|
||
typedef void (*SentryRegisterImageCallback)(const struct mach_header *mh, intptr_t vmaddr_slide); | ||
typedef void (*SentryRegisterFunction)(SentryRegisterImageCallback function); | ||
|
||
static SentryRegisterFunction _sentry_register_func_for_add_image | ||
= &_dyld_register_func_for_add_image; | ||
static SentryRegisterFunction _sentry_register_func_for_remove_image | ||
= &_dyld_register_func_for_remove_image; | ||
|
||
static void (*SentryWillAddImageCallback)(void) = NULL; | ||
|
||
void | ||
sentry_setRegisterFuncForAddImage(SentryRegisterFunction addFunction) | ||
{ | ||
_sentry_register_func_for_add_image = addFunction; | ||
} | ||
|
||
void | ||
sentry_setRegisterFuncForRemoveImage(SentryRegisterFunction removeFunction) | ||
{ | ||
_sentry_register_func_for_remove_image = removeFunction; | ||
} | ||
|
||
void | ||
sentry_setFuncForBeforeAdd(void (*callback)(void)) | ||
{ | ||
SentryWillAddImageCallback = callback; | ||
} | ||
|
||
void | ||
sentry_resetFuncForAddRemoveImage(void) | ||
{ | ||
_sentry_register_func_for_add_image = &_dyld_register_func_for_add_image; | ||
_sentry_register_func_for_remove_image = &_dyld_register_func_for_remove_image; | ||
} | ||
|
||
# define sentry_dyld_register_func_for_add_image(CALLBACK) \ | ||
_sentry_register_func_for_add_image(CALLBACK); | ||
# define sentry_dyld_register_func_for_remove_image(CALLBACK) \ | ||
_sentry_register_func_for_remove_image(CALLBACK); | ||
# define _will_add_image() \ | ||
if (SentryWillAddImageCallback) \ | ||
SentryWillAddImageCallback(); | ||
#else | ||
# define sentry_dyld_register_func_for_add_image(CALLBACK) \ | ||
_dyld_register_func_for_add_image(CALLBACK) | ||
# define sentry_dyld_register_func_for_remove_image(CALLBACK) \ | ||
_dyld_register_func_for_remove_image(CALLBACK) | ||
# define _will_add_image() | ||
#endif | ||
|
||
typedef struct SentryCrashBinaryImageNode { | ||
SentryCrashBinaryImage image; | ||
bool available; | ||
struct SentryCrashBinaryImageNode *next; | ||
} SentryCrashBinaryImageNode; | ||
|
||
static SentryCrashBinaryImageNode rootNode = { 0 }; | ||
static SentryCrashBinaryImageNode *tailNode = NULL; | ||
static pthread_mutex_t binaryImagesMutex = PTHREAD_MUTEX_INITIALIZER; | ||
|
||
static void | ||
binaryImageAdded(const struct mach_header *header, intptr_t slide) | ||
{ | ||
if (tailNode == NULL) { | ||
return; | ||
} | ||
|
||
Dl_info info; | ||
if (!dladdr(header, &info) || info.dli_fname == NULL) { | ||
return; | ||
} | ||
|
||
SentryCrashBinaryImage binaryImage = { 0 }; | ||
if (!sentrycrashdl_getBinaryImageForHeader( | ||
(const void *)header, info.dli_fname, &binaryImage, false)) { | ||
return; | ||
} | ||
|
||
SentryCrashBinaryImageNode *newNode = malloc(sizeof(SentryCrashBinaryImageNode)); | ||
newNode->available = true; | ||
newNode->image = binaryImage; | ||
newNode->next = NULL; | ||
_will_add_image(); | ||
pthread_mutex_lock(&binaryImagesMutex); | ||
// Recheck tailNode as it could be null when | ||
// stopped from another thread. | ||
if (tailNode != NULL) { | ||
tailNode->next = newNode; | ||
tailNode = tailNode->next; | ||
} else { | ||
free(newNode); | ||
} | ||
pthread_mutex_unlock(&binaryImagesMutex); | ||
} | ||
|
||
static void | ||
binaryImageRemoved(const struct mach_header *header, intptr_t slide) | ||
{ | ||
SentryCrashBinaryImageNode *nextNode = &rootNode; | ||
|
||
while (nextNode != NULL) { | ||
if (nextNode->image.address == (uint64_t)header) { | ||
nextNode->available = false; | ||
break; | ||
} | ||
nextNode = nextNode->next; | ||
} | ||
} | ||
|
||
void | ||
sentrycrashbic_iterateOverImages(sentrycrashbic_imageIteratorCallback callback, void *context) | ||
{ | ||
/** | ||
We can't use locks here because this is meant to be used during crashes, | ||
where we can't use async unsafe functions. In order to avoid potential problems, | ||
we choose an approach that doesn't remove nodes from the list. | ||
*/ | ||
SentryCrashBinaryImageNode *nextNode = &rootNode; | ||
|
||
// If tailNode is null it means the cache was stopped, therefore we end the iteration. | ||
// This will minimize any race condition effect without the need for locks. | ||
while (nextNode != NULL && tailNode != NULL) { | ||
if (nextNode->available) { | ||
callback(&nextNode->image, context); | ||
} | ||
nextNode = nextNode->next; | ||
} | ||
} | ||
|
||
void | ||
sentrycrashbic_startCache(void) | ||
{ | ||
pthread_mutex_lock(&binaryImagesMutex); | ||
if (tailNode != NULL) { | ||
// Already initialized | ||
pthread_mutex_unlock(&binaryImagesMutex); | ||
return; | ||
} | ||
tailNode = &rootNode; | ||
rootNode.next = NULL; | ||
pthread_mutex_unlock(&binaryImagesMutex); | ||
|
||
// During a call to _dyld_register_func_for_add_image() the callback func is called for every | ||
// existing image | ||
sentry_dyld_register_func_for_add_image(&binaryImageAdded); | ||
sentry_dyld_register_func_for_remove_image(&binaryImageRemoved); | ||
} | ||
|
||
void | ||
sentrycrashbic_stopCache(void) | ||
{ | ||
pthread_mutex_lock(&binaryImagesMutex); | ||
if (tailNode == NULL) { | ||
pthread_mutex_unlock(&binaryImagesMutex); | ||
return; | ||
} | ||
|
||
SentryCrashBinaryImageNode *node = rootNode.next; | ||
rootNode.next = NULL; | ||
tailNode = NULL; | ||
|
||
while (node != NULL) { | ||
SentryCrashBinaryImageNode *nextNode = node->next; | ||
free(node); | ||
node = nextNode; | ||
} | ||
|
||
pthread_mutex_unlock(&binaryImagesMutex); | ||
} |
22 changes: 22 additions & 0 deletions
22
Sources/SentryCrash/Recording/SentryCrashBinaryImageCache.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
#ifndef SentryCrashBinaryImageCache_h | ||
#define SentryCrashBinaryImageCache_h | ||
|
||
#include "SentryCrashDynamicLinker.h" | ||
#include <stdio.h> | ||
|
||
typedef void (*sentrycrashbic_imageIteratorCallback)(SentryCrashBinaryImage *, void *context); | ||
|
||
void sentrycrashbic_iterateOverImages(sentrycrashbic_imageIteratorCallback index, void *context); | ||
|
||
/** | ||
* Starts the cache that will monitor binary image being loaded or removed. | ||
*/ | ||
void sentrycrashbic_startCache(void); | ||
|
||
/** | ||
* Stops the cache from monitoring binary image being loaded or removed. | ||
* This will also clean the cache. | ||
*/ | ||
void sentrycrashbic_stopCache(void); | ||
|
||
#endif /* SentryCrashBinaryImageCache_h */ |
Oops, something went wrong.