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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 66 additions & 25 deletions backtrace-library/src/main/cpp/backends/crashpad-backend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@
#include "handler/handler_main.h"
#include "handler/crash_report_upload_thread.h"
#include "backtrace-native.h"
#include "client/annotation.h"
#include "snapshot/snapshot_constants.h"
#include <jni.h>
#include <libgen.h>
#include <cstring>
#include <unordered_map>

extern std::string thread_id;
extern std::atomic_bool initialized;
Expand Down Expand Up @@ -56,6 +60,61 @@ namespace {
"Started Crashpad upload thread for offline native reports");
}

// Stores attributes as crashpad StringAnnotations, replacing the 64-entry
// simple_annotations dictionary. The snapshot reader reads at most
// crashpad::kMaxNumberOfAnnotations per module, so SetAnnotation drops new
// keys past that. Annotations and name buffers are allocated once and never
// freed (the global list references them for the process lifetime).
constexpr crashpad::Annotation::ValueSizeType kAnnotationValueMaxSize = 4096;
using DynamicAnnotation = crashpad::StringAnnotation<kAnnotationValueMaxSize>;

std::unordered_map<std::string, DynamicAnnotation*> g_annotations;

// Caller must hold attribute_synchronization.
DynamicAnnotation* GetOrCreateAnnotation(const std::string& key) {
auto it = g_annotations.find(key);
if (it != g_annotations.end()) {
return it->second;
}
char* nameCopy = new char[key.size() + 1];
std::memcpy(nameCopy, key.c_str(), key.size() + 1);
DynamicAnnotation* annotation = new DynamicAnnotation(nameCopy);
g_annotations.emplace(key, annotation);
return annotation;
}

void SetAnnotation(const char* rawKey, const char* rawValue) {
Comment thread
KishanPRao marked this conversation as resolved.
if (rawKey == nullptr || rawKey[0] == '\0') {
return;
}
const std::lock_guard<std::mutex> lock(attribute_synchronization);
std::string key(rawKey);
// Drop new keys past the read limit; updates to existing keys are fine.
if (g_annotations.find(key) == g_annotations.end()
&& g_annotations.size() >= crashpad::kMaxNumberOfAnnotations) {
__android_log_print(
ANDROID_LOG_WARN,
"Backtrace-Android",
"Dropping native attribute '%s': reached crashpad's annotation read limit",
rawKey);
return;
}
DynamicAnnotation* annotation = GetOrCreateAnnotation(key);
// Set(const char*) uses strncpy; the StringPiece overload uses std::copy.
annotation->Set(base::StringPiece(rawValue != nullptr ? rawValue : ""));
}

void ClearAnnotation(const char* rawKey) {
if (rawKey == nullptr || rawKey[0] == '\0') {
return;
}
const std::lock_guard<std::mutex> lock(attribute_synchronization);
auto it = g_annotations.find(std::string(rawKey));
if (it != g_annotations.end()) {
it->second->Clear();
}
}

} // namespace

std::vector<std::string>
Expand Down Expand Up @@ -325,38 +384,29 @@ void DumpWithoutCrashCrashpad(jstring message, jboolean set_main_thread_as_fault
crashpad::CaptureContext(&context);

// set dump message for single report
crashpad::SimpleStringDictionary *annotations = NULL;

if (message != NULL || set_main_thread_as_faulting_thread == true) {
JNIEnv *env = GetJniEnv();
if (env == nullptr) {
__android_log_print(ANDROID_LOG_ERROR, "Backtrace-Android", "Cannot initialize JNIEnv");
return;
}
const std::lock_guard<std::mutex> lock(attribute_synchronization);
crashpad::CrashpadInfo *info = crashpad::CrashpadInfo::GetCrashpadInfo();
annotations = info->simple_annotations();
if (!annotations) {
annotations = new crashpad::SimpleStringDictionary();
info->set_simple_annotations(annotations);
}
if (set_main_thread_as_faulting_thread == true) {
annotations->SetKeyValue("_mod_faulting_tid", thread_id);
SetAnnotation("_mod_faulting_tid", thread_id.c_str());
}
if (message != NULL) {
// user can't override error.message - exception message that Crashpad/crash-reporting tool
// will set to tell user about error message. This code will set error.message only for single
// report and after creating a dump, method will clean up this attribute.
jboolean isCopy;
const char *rawMessage = env->GetStringUTFChars(message, &isCopy);
annotations->SetKeyValue("error.message", rawMessage);
SetAnnotation("error.message", rawMessage);
env->ReleaseStringUTFChars(message, rawMessage);
}
}
client->DumpWithoutCrash(&context);

if (annotations != NULL) {
annotations->RemoveKey("error.message");
if (message != NULL) {
ClearAnnotation("error.message");
}
}

Expand All @@ -372,22 +422,13 @@ void AddAttributeCrashpad(jstring key, jstring value) {
return;
}

const std::lock_guard<std::mutex> lock(attribute_synchronization);
crashpad::CrashpadInfo *info = crashpad::CrashpadInfo::GetCrashpadInfo();
crashpad::SimpleStringDictionary *annotations = info->simple_annotations();
if (!annotations) {
annotations = new crashpad::SimpleStringDictionary();
info->set_simple_annotations(annotations);
}

jboolean isCopy;
const char *crashpadKey = env->GetStringUTFChars(key, &isCopy);
const char *crashpadValue = env->GetStringUTFChars(value, &isCopy);
if (crashpadKey && crashpadValue)
annotations->SetKeyValue(crashpadKey, crashpadValue);
SetAnnotation(crashpadKey, crashpadValue);

env->ReleaseStringUTFChars(key, crashpadKey);
env->ReleaseStringUTFChars(value, crashpadValue);
if (crashpadKey) env->ReleaseStringUTFChars(key, crashpadKey);
if (crashpadValue) env->ReleaseStringUTFChars(value, crashpadValue);
}

void AddAttachmentCrashpad(jstring jattachment) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,9 @@ public Map<String, Object> getAttributes() {
* - is not an object (the attribute value is primitive type like String, or
* Int)
*
* Note: native crash reports omit attributes with an empty-string value
* (managed/JVM reports are unaffected).
*
* @param key attribute name
* @param value attribute value.
*/
Expand Down
Loading