I am getting incorrect stack traces for the following code. The curious thing is presence of this unused function: uncommenting_this_functions_scuppers_stackrace_for_release_builds
(obviously christened for this example.).
If this is present, wrong stack trace. If absent, somewhat better stack trace. In the incorrect one, function names are not coming out correctly.
We have a much larger Linux C++ projects where stack traces are jumbled up and trying to narrow down the problem with a toy example like this.
// adapted from https://github.com/getsentry/sentry-native/blob/c3b7b07c99d19f39a4910093378e29bba86fde37/examples/example.c
// usage
// ./sentry_example log crash
// or
// ./sentry_example log throw-exception
#include "sentry.h"
#include <unistd.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
static void print_envelope(sentry_envelope_t *envelope, void *unused_state)
{
(void) unused_state;
size_t size_out = 0;
char *s = sentry_envelope_serialize(envelope, &size_out);
printf("%s", s);
sentry_free(s);
sentry_envelope_free(envelope);
}
static bool has_arg(int argc, char **argv, const char *arg)
{
for (int i = 1; i < argc; i++)
{
if (strcmp(argv[i], arg) == 0)
{
return true;
}
}
return false;
}
//--------------------------- exception
static void trigger_exception(volatile int *v)
{
*v = *v + 2;
std::cout << "trigger_exception " << *v << std::endl;
throw std::runtime_error("Test throw runtime error");
}
static void trigger_exception_b(volatile int *v)
{
*v = *v + 2;
std::cout << "trigger_exception_b calling trigger_exception " << *v << std::endl;
trigger_exception(v);
}
static void trigger_exception_a(volatile int *v)
{
*v = *v + 2;
std::cout << "trigger_exception_a calling trigger_exception_b " << *v << std::endl;
trigger_exception_b(v);
}
//--------------------------- crash
static void *invalid_mem = (void *)1;
static void trigger_crash(int *v)
{
*v = *v + 2;
std::cout << "trigger_crash " << std::endl;
memset((char *) invalid_mem, 1, 100);
}
static void trigger_crash_b(int *v)
{
*v = *v + 2;
std::cout << "trigger_crash_b calling trigger_crash " << *v << std::endl;
trigger_crash(v);
}
static void trigger_crash_a(int *v)
{
*v = *v + 2;
std::cout << "trigger_crash_a calling trigger_crash_b " << *v << std::endl;
trigger_crash_b(v);
}
//-------------------- sentry setup class
// THIS PROBEM BELOW OCCURS WITH 'Release' NOT 'Debug'
/*
void uncommenting_this_functions_scuppers_stackrace_for_release_builds(int argc, char **argv)
{
if (has_arg(argc, argv, "crash"))
{
int val = 1;
trigger_crash_a(&val); // ******* Crashpad bug: presence of this scuppers stack trace for 'crash'
trigger_exception_a(&val); // ******* Crashpad bug: presence of this scuppers stack trace for 'exception'
std::cout << "trigger_crash_a call with " << val << std::endl;
}
}
*/
struct SetupSentry
{
SetupSentry(int argc, char **argv)
{
sentry_options_t *options = sentry_options_new();
// crash upload data location for controller
sentry_options_set_database_path(options, ".sentry-native");
const char *const crashHandlerPath = "/usr/bin/crashpad_handler";
sentry_options_set_handler_path(options, crashHandlerPath);
sentry_options_set_auto_session_tracking(options, false);
sentry_options_set_symbolize_stacktraces(options, true);
// using project: sentry_example_docker
sentry_options_set_dsn(options, <dsn-value-to-be-supplied>);
sentry_options_set_environment(options, "development");
// sentry defaults this to the `SENTRY_RELEASE` env variable
if (!has_arg(argc, argv, "release-env"))
{
sentry_options_set_release(options, "test-example-release");
}
{
sentry_options_set_debug(options, 1);
}
if (has_arg(argc, argv, "attachment"))
{
// assuming the example / test is run directly from the cmake build
// directory
sentry_options_add_attachment(options, "./CMakeCache.txt");
}
if (has_arg(argc, argv, "stdout"))
{
sentry_options_set_transport(
options, sentry_transport_new(print_envelope));
}
sentry_init(options);
if (!has_arg(argc, argv, "no-setup"))
{
sentry_set_transaction("test-transaction");
sentry_set_level(SENTRY_LEVEL_WARNING);
sentry_set_extra("extra stuff", sentry_value_new_string("some value"));
sentry_set_extra("…unicode key…",
// https://xkcd.com/1813/ :-)
sentry_value_new_string("őá…–🤮🚀¿ 한글 테스트"));
sentry_set_tag("expected-tag", "some value");
sentry_set_tag("not-expected-tag", "some value");
sentry_remove_tag("not-expected-tag");
sentry_value_t context = sentry_value_new_object();
sentry_value_set_by_key(
context, "type", sentry_value_new_string("runtime"));
sentry_value_set_by_key(
context, "name", sentry_value_new_string("testing-runtime"));
sentry_set_context("runtime", context);
sentry_value_t user = sentry_value_new_object();
sentry_value_set_by_key(user, "id", sentry_value_new_int32(42));
sentry_value_set_by_key(
user, "username", sentry_value_new_string("some_name"));
sentry_set_user(user);
sentry_value_t default_crumb
= sentry_value_new_breadcrumb(NULL, "default level is info");
sentry_add_breadcrumb(default_crumb);
sentry_value_t debug_crumb
= sentry_value_new_breadcrumb("http", "debug crumb");
sentry_value_set_by_key(
debug_crumb, "category", sentry_value_new_string("example!"));
sentry_value_set_by_key(
debug_crumb, "level", sentry_value_new_string("debug"));
sentry_add_breadcrumb(debug_crumb);
sentry_value_t nl_crumb
= sentry_value_new_breadcrumb(NULL, "lf\ncrlf\r\nlf\n...");
sentry_value_set_by_key(
nl_crumb, "category", sentry_value_new_string("something else"));
sentry_add_breadcrumb(nl_crumb);
}
if (has_arg(argc, argv, "start-session"))
{
sentry_start_session();
}
if (has_arg(argc, argv, "overflow-breadcrumbs"))
{
for (size_t i = 0; i < 101; i++)
{
char buffer[4];
snprintf(buffer, 4, "%zu", i);
sentry_add_breadcrumb(sentry_value_new_breadcrumb(0, buffer));
}
}
if (has_arg(argc, argv, "capture-multiple"))
{
for (size_t i = 0; i < 10; i++)
{
char buffer[10];
snprintf(buffer, 10, "Event #%zu", i);
sentry_value_t event = sentry_value_new_message_event(
SENTRY_LEVEL_INFO, NULL, buffer);
sentry_capture_event(event);
}
}
if (has_arg(argc, argv, "crash"))
{
int val = 1;
trigger_crash_a(&val);
}
if (has_arg(argc, argv, "capture-event"))
{
sentry_value_t event = sentry_value_new_message_event(
SENTRY_LEVEL_INFO, "my-logger", "Hello World!");
if (has_arg(argc, argv, "add-stacktrace"))
{
sentry_event_value_add_stacktrace(event, NULL, 0);
}
sentry_capture_event(event);
}
if (has_arg(argc, argv, "capture-exception"))
{
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_event_add_exception(event, exc);
sentry_capture_event(event);
}
if (has_arg(argc, argv, "throw-exception"))
{
int val = 36;
trigger_exception_a(&val);
}
}
~SetupSentry()
{
sentry_close();
}
};
int main(int argc, char **argv)
{
std::cout << "Starting crashpad example ---- \n";
// one can trigger crash by supplying crash log as args
// eg: ./sentry_example log crash
// ./sentry_example log throw-exception
//
SetupSentry ss(argc, argv);
}
The call sequence leading to crash should be : main → SetupSentry::SetupSentry → trigger_crash_a → trigger_crash_b → trigger_crash
See the results:
Correct stack trace image (but several __fini calls interspersed)
Incorrect stack trace image (called function names not appearing as well)
In our build (using docker), the debug files are uploaded as below
objcopy --only-keep-debug --compress-debug-sections=zlib sentry_example sentry_example_crashpad_docker.debug
sentry-cli upload-dif <org slug> sentry_example_crashpad_docker.debug