22#include " handler/handler_main.h"
33#include " handler/crash_report_upload_thread.h"
44#include " backtrace-native.h"
5+ #include " client/annotation.h"
6+ #include " snapshot/snapshot_constants.h"
57#include < jni.h>
68#include < libgen.h>
9+ #include < cstring>
10+ #include < unordered_map>
711
812extern std::string thread_id;
913extern std::atomic_bool initialized;
@@ -56,6 +60,78 @@ namespace {
5660 " Started Crashpad upload thread for offline native reports" );
5761 }
5862
63+ // Stores attributes as crashpad StringAnnotations, replacing the 64-entry
64+ // simple_annotations dictionary. The crashpad snapshot reader reads at most
65+ // crashpad::kMaxNumberOfAnnotations annotation objects per module, so
66+ // SetAnnotation drops new user keys past that limit (internal keys keep
67+ // reserved slots). Annotations and name buffers are allocated once and never
68+ // freed (the global list references them for the process lifetime).
69+ constexpr crashpad::Annotation::ValueSizeType kAnnotationValueMaxSize = 4096 ;
70+ using DynamicAnnotation = crashpad::StringAnnotation<kAnnotationValueMaxSize >;
71+
72+ // Reserve slots for internal, per-report annotations so they are never
73+ // crowded out by user attributes near the read limit.
74+ constexpr size_t kReservedAnnotationSlots = 2 ; // error.message, _mod_faulting_tid
75+
76+ bool IsValidAnnotationKey (const char * rawKey) {
77+ return rawKey != nullptr && rawKey[0 ] != ' \0 ' ;
78+ }
79+
80+ bool IsReservedAnnotationKey (const std::string& key) {
81+ return key == " error.message" || key == " _mod_faulting_tid" ;
82+ }
83+
84+ std::unordered_map<std::string, DynamicAnnotation*> g_annotations;
85+
86+ // Caller must hold attribute_synchronization.
87+ DynamicAnnotation* GetOrCreateAnnotation (const std::string& key) {
88+ auto it = g_annotations.find (key);
89+ if (it != g_annotations.end ()) {
90+ return it->second ;
91+ }
92+ char * nameCopy = new char [key.size () + 1 ];
93+ std::memcpy (nameCopy, key.c_str (), key.size () + 1 );
94+ DynamicAnnotation* annotation = new DynamicAnnotation (nameCopy);
95+ g_annotations.emplace (key, annotation);
96+ return annotation;
97+ }
98+
99+ void SetAnnotation (const char * rawKey, const char * rawValue) {
100+ if (!IsValidAnnotationKey (rawKey)) {
101+ return ;
102+ }
103+ const std::lock_guard<std::mutex> lock (attribute_synchronization);
104+ std::string key (rawKey);
105+ const bool isNewKey = g_annotations.find (key) == g_annotations.end ();
106+ // Cap user keys below the read limit so the reserved internal keys can
107+ // always be registered; updates to existing keys are always allowed.
108+ const size_t userAnnotationLimit =
109+ crashpad::kMaxNumberOfAnnotations - kReservedAnnotationSlots ;
110+ if (isNewKey && !IsReservedAnnotationKey (key)
111+ && g_annotations.size () >= userAnnotationLimit) {
112+ __android_log_print (
113+ ANDROID_LOG_WARN ,
114+ " Backtrace-Android" ,
115+ " Dropping native attribute '%s': reached crashpad's annotation read limit" ,
116+ rawKey);
117+ return ;
118+ }
119+ DynamicAnnotation* annotation = GetOrCreateAnnotation (key);
120+ // Set(const char*) uses strncpy; the StringPiece overload uses std::copy.
121+ annotation->Set (base::StringPiece (rawValue != nullptr ? rawValue : " " ));
122+ }
123+
124+ void ClearAnnotation (const char * rawKey) {
125+ if (rawKey == nullptr || rawKey[0 ] == ' \0 ' ) {
126+ return ;
127+ }
128+ const std::lock_guard<std::mutex> lock (attribute_synchronization);
129+ auto it = g_annotations.find (std::string (rawKey));
130+ if (it != g_annotations.end ()) {
131+ it->second ->Clear ();
132+ }
133+ }
134+
59135} // namespace
60136
61137std::vector<std::string>
@@ -325,38 +401,29 @@ void DumpWithoutCrashCrashpad(jstring message, jboolean set_main_thread_as_fault
325401 crashpad::CaptureContext (&context);
326402
327403 // set dump message for single report
328- crashpad::SimpleStringDictionary *annotations = NULL ;
329-
330404 if (message != NULL || set_main_thread_as_faulting_thread == true ) {
331405 JNIEnv *env = GetJniEnv ();
332406 if (env == nullptr ) {
333407 __android_log_print (ANDROID_LOG_ERROR , " Backtrace-Android" , " Cannot initialize JNIEnv" );
334408 return ;
335409 }
336- const std::lock_guard<std::mutex> lock (attribute_synchronization);
337- crashpad::CrashpadInfo *info = crashpad::CrashpadInfo::GetCrashpadInfo ();
338- annotations = info->simple_annotations ();
339- if (!annotations) {
340- annotations = new crashpad::SimpleStringDictionary ();
341- info->set_simple_annotations (annotations);
342- }
343410 if (set_main_thread_as_faulting_thread == true ) {
344- annotations-> SetKeyValue (" _mod_faulting_tid" , thread_id);
411+ SetAnnotation (" _mod_faulting_tid" , thread_id. c_str () );
345412 }
346413 if (message != NULL ) {
347414 // user can't override error.message - exception message that Crashpad/crash-reporting tool
348415 // will set to tell user about error message. This code will set error.message only for single
349416 // report and after creating a dump, method will clean up this attribute.
350417 jboolean isCopy;
351418 const char *rawMessage = env->GetStringUTFChars (message, &isCopy);
352- annotations-> SetKeyValue (" error.message" , rawMessage);
419+ SetAnnotation (" error.message" , rawMessage);
353420 env->ReleaseStringUTFChars (message, rawMessage);
354421 }
355422 }
356423 client->DumpWithoutCrash (&context);
357424
358- if (annotations != NULL ) {
359- annotations-> RemoveKey (" error.message" );
425+ if (message != NULL ) {
426+ ClearAnnotation (" error.message" );
360427 }
361428}
362429
@@ -372,22 +439,13 @@ void AddAttributeCrashpad(jstring key, jstring value) {
372439 return ;
373440 }
374441
375- const std::lock_guard<std::mutex> lock (attribute_synchronization);
376- crashpad::CrashpadInfo *info = crashpad::CrashpadInfo::GetCrashpadInfo ();
377- crashpad::SimpleStringDictionary *annotations = info->simple_annotations ();
378- if (!annotations) {
379- annotations = new crashpad::SimpleStringDictionary ();
380- info->set_simple_annotations (annotations);
381- }
382-
383442 jboolean isCopy;
384443 const char *crashpadKey = env->GetStringUTFChars (key, &isCopy);
385444 const char *crashpadValue = env->GetStringUTFChars (value, &isCopy);
386- if (crashpadKey && crashpadValue)
387- annotations->SetKeyValue (crashpadKey, crashpadValue);
445+ SetAnnotation (crashpadKey, crashpadValue);
388446
389- env->ReleaseStringUTFChars (key, crashpadKey);
390- env->ReleaseStringUTFChars (value, crashpadValue);
447+ if (crashpadKey) env->ReleaseStringUTFChars (key, crashpadKey);
448+ if (crashpadValue) env->ReleaseStringUTFChars (value, crashpadValue);
391449}
392450
393451void AddAttachmentCrashpad (jstring jattachment) {
0 commit comments