Breakpad, a crash reporter, from Google.
Original home: https://chromium.googlesource.com/breakpad/breakpad/
修订版 | 8e9080bc533e6fe8602629ede8a2d866a1f7f1eb (tree) |
---|---|
时间 | 2017-11-04 02:06:05 |
作者 | Yi Wang <jswang@goog...> |
Commiter | Mark Mentovai |
Create LongStringDictionary and replace SimpleStringDictionary on iOS
This relands fd0a0d2b7ae9dd3d8a02b6a12e7941f7189fbb6c which was reverted
in 5dad29423e62292c6ff468cabfee4422ba55b18b, with a fix for guarding
kMaxSuffixLength which only used in assert()s with macros which breaks
chromium.mac/ios-device.
Change-Id: I5ee21b7f290517d6e7a0ef90b693b97f92392549
Reviewed-on: https://chromium-review.googlesource.com/751922
Reviewed-by: Mark Mentovai <mark@chromium.org>
@@ -38,13 +38,15 @@ | ||
38 | 38 | #include <sys/sysctl.h> |
39 | 39 | #include <TargetConditionals.h> |
40 | 40 | |
41 | +#include <string> | |
42 | + | |
41 | 43 | #import "client/ios/handler/ios_exception_minidump_generator.h" |
42 | 44 | #import "client/mac/crash_generation/ConfigFile.h" |
43 | 45 | #import "client/mac/handler/exception_handler.h" |
44 | 46 | #import "client/mac/handler/minidump_generator.h" |
45 | -#import "client/mac/sender/uploader.h" | |
46 | 47 | #import "client/mac/handler/protected_memory_allocator.h" |
47 | -#import "common/simple_string_dictionary.h" | |
48 | +#import "client/mac/sender/uploader.h" | |
49 | +#import "common/long_string_dictionary.h" | |
48 | 50 | |
49 | 51 | #if !TARGET_OS_TV && !TARGET_OS_WATCH |
50 | 52 | #import "client/mac/handler/exception_handler.h" |
@@ -66,7 +68,7 @@ | ||
66 | 68 | |
67 | 69 | using google_breakpad::ConfigFile; |
68 | 70 | using google_breakpad::EnsureDirectoryPathExists; |
69 | -using google_breakpad::SimpleStringDictionary; | |
71 | +using google_breakpad::LongStringDictionary; | |
70 | 72 | |
71 | 73 | //============================================================================= |
72 | 74 | // We want any memory allocations which are used by breakpad during the |
@@ -197,7 +199,7 @@ class Breakpad { | ||
197 | 199 | // MachineExceptions.h, we have to explicitly name the handler. |
198 | 200 | google_breakpad::ExceptionHandler *handler_; // The actual handler (STRONG) |
199 | 201 | |
200 | - SimpleStringDictionary *config_params_; // Create parameters (STRONG) | |
202 | + LongStringDictionary *config_params_; // Create parameters (STRONG) | |
201 | 203 | |
202 | 204 | ConfigFile config_file_; |
203 | 205 |
@@ -313,7 +315,7 @@ Breakpad::~Breakpad() { | ||
313 | 315 | // since they were allocated by ProtectedMemoryAllocator objects. |
314 | 316 | // |
315 | 317 | if (config_params_) { |
316 | - config_params_->~SimpleStringDictionary(); | |
318 | + config_params_->~LongStringDictionary(); | |
317 | 319 | } |
318 | 320 | |
319 | 321 | if (handler_) |
@@ -381,10 +383,10 @@ bool Breakpad::ExtractParameters(NSDictionary *parameters) { | ||
381 | 383 | } |
382 | 384 | |
383 | 385 | config_params_ = |
384 | - new (gKeyValueAllocator->Allocate(sizeof(SimpleStringDictionary)) ) | |
385 | - SimpleStringDictionary(); | |
386 | + new (gKeyValueAllocator->Allocate(sizeof(LongStringDictionary))) | |
387 | + LongStringDictionary(); | |
386 | 388 | |
387 | - SimpleStringDictionary &dictionary = *config_params_; | |
389 | + LongStringDictionary &dictionary = *config_params_; | |
388 | 390 | |
389 | 391 | dictionary.SetKeyValue(BREAKPAD_SERVER_TYPE, [serverType UTF8String]); |
390 | 392 | dictionary.SetKeyValue(BREAKPAD_PRODUCT_DISPLAY, [display UTF8String]); |
@@ -427,8 +429,8 @@ NSString *Breakpad::KeyValue(NSString *key) { | ||
427 | 429 | if (!config_params_ || !key) |
428 | 430 | return nil; |
429 | 431 | |
430 | - const char *value = config_params_->GetValueForKey([key UTF8String]); | |
431 | - return value ? [NSString stringWithUTF8String:value] : nil; | |
432 | + const std::string value = config_params_->GetValueForKey([key UTF8String]); | |
433 | + return value.empty() ? nil : [NSString stringWithUTF8String:value.c_str()]; | |
432 | 434 | } |
433 | 435 | |
434 | 436 | //============================================================================= |
@@ -502,8 +504,8 @@ void Breakpad::UploadData(NSData *data, NSString *name, | ||
502 | 504 | NSDictionary *server_parameters) { |
503 | 505 | NSMutableDictionary *config = [NSMutableDictionary dictionary]; |
504 | 506 | |
505 | - SimpleStringDictionary::Iterator it(*config_params_); | |
506 | - while (const SimpleStringDictionary::Entry *next = it.Next()) { | |
507 | + LongStringDictionary::Iterator it(*config_params_); | |
508 | + while (const LongStringDictionary::Entry *next = it.Next()) { | |
507 | 509 | [config setValue:[NSString stringWithUTF8String:next->value] |
508 | 510 | forKey:[NSString stringWithUTF8String:next->key]]; |
509 | 511 | } |
@@ -532,7 +534,7 @@ NSDictionary *Breakpad::GenerateReport(NSDictionary *server_parameters) { | ||
532 | 534 | if (!success) |
533 | 535 | return nil; |
534 | 536 | |
535 | - SimpleStringDictionary params = *config_params_; | |
537 | + LongStringDictionary params = *config_params_; | |
536 | 538 | for (NSString *key in server_parameters) { |
537 | 539 | params.SetKeyValue([key UTF8String], |
538 | 540 | [[server_parameters objectForKey:key] UTF8String]); |
@@ -567,7 +569,7 @@ bool Breakpad::HandleMinidump(const char *dump_dir, | ||
567 | 569 | void Breakpad::HandleUncaughtException(NSException *exception) { |
568 | 570 | // Generate the minidump. |
569 | 571 | google_breakpad::IosExceptionMinidumpGenerator generator(exception); |
570 | - const char *minidump_path = | |
572 | + const std::string minidump_path = | |
571 | 573 | config_params_->GetValueForKey(BREAKPAD_DUMP_DIRECTORY); |
572 | 574 | std::string minidump_id; |
573 | 575 | std::string minidump_filename = generator.UniqueNameInDirectory(minidump_path, |
@@ -580,7 +582,7 @@ void Breakpad::HandleUncaughtException(NSException *exception) { | ||
580 | 582 | // 2- If the application crash while trying to handle this exception, a usual |
581 | 583 | // report will be generated. This report must not contain these special |
582 | 584 | // keys. |
583 | - SimpleStringDictionary params = *config_params_; | |
585 | + LongStringDictionary params = *config_params_; | |
584 | 586 | params.SetKeyValue(BREAKPAD_SERVER_PARAMETER_PREFIX "type", "exception"); |
585 | 587 | params.SetKeyValue(BREAKPAD_SERVER_PARAMETER_PREFIX "exceptionName", |
586 | 588 | [[exception name] UTF8String]); |
@@ -589,9 +591,9 @@ void Breakpad::HandleUncaughtException(NSException *exception) { | ||
589 | 591 | |
590 | 592 | // And finally write the config file. |
591 | 593 | ConfigFile config_file; |
592 | - config_file.WriteFile(minidump_path, | |
594 | + config_file.WriteFile(minidump_path.c_str(), | |
593 | 595 | ¶ms, |
594 | - minidump_path, | |
596 | + minidump_path.c_str(), | |
595 | 597 | minidump_id.c_str()); |
596 | 598 | } |
597 | 599 |
@@ -619,9 +621,9 @@ BreakpadRef BreakpadCreate(NSDictionary *parameters) { | ||
619 | 621 | |
620 | 622 | gKeyValueAllocator = |
621 | 623 | new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator))) |
622 | - ProtectedMemoryAllocator(sizeof(SimpleStringDictionary)); | |
624 | + ProtectedMemoryAllocator(sizeof(LongStringDictionary)); | |
623 | 625 | |
624 | - // Create a mutex for use in accessing the SimpleStringDictionary | |
626 | + // Create a mutex for use in accessing the LongStringDictionary | |
625 | 627 | int mutexResult = pthread_mutex_init(&gDictionaryMutex, NULL); |
626 | 628 | if (mutexResult == 0) { |
627 | 629 |
@@ -57,6 +57,8 @@ | ||
57 | 57 | 1EEEB6101720821900F7E689 /* simple_string_dictionary.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EEEB60D1720821900F7E689 /* simple_string_dictionary.h */; }; |
58 | 58 | AA747D9F0F9514B9006C5449 /* Breakpad_Prefix.pch in Headers */ = {isa = PBXBuildFile; fileRef = AA747D9E0F9514B9006C5449 /* Breakpad_Prefix.pch */; }; |
59 | 59 | AACBBE4A0F95108600F1A2B1 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AACBBE490F95108600F1A2B1 /* Foundation.framework */; }; |
60 | + CF6D547D1F9E6FFE00E95174 /* long_string_dictionary.cc in Sources */ = {isa = PBXBuildFile; fileRef = CF6D547C1F9E6FFE00E95174 /* long_string_dictionary.cc */; }; | |
61 | + CF706DC11F7C6EFB002C54C7 /* long_string_dictionary.h in Headers */ = {isa = PBXBuildFile; fileRef = CF706DC01F7C6EFB002C54C7 /* long_string_dictionary.h */; }; | |
60 | 62 | /* End PBXBuildFile section */ |
61 | 63 | |
62 | 64 | /* Begin PBXFileReference section */ |
@@ -111,6 +113,8 @@ | ||
111 | 113 | 1EEEB60D1720821900F7E689 /* simple_string_dictionary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = simple_string_dictionary.h; sourceTree = "<group>"; }; |
112 | 114 | AA747D9E0F9514B9006C5449 /* Breakpad_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Breakpad_Prefix.pch; sourceTree = SOURCE_ROOT; }; |
113 | 115 | AACBBE490F95108600F1A2B1 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; |
116 | + CF6D547C1F9E6FFE00E95174 /* long_string_dictionary.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = long_string_dictionary.cc; sourceTree = "<group>"; }; | |
117 | + CF706DC01F7C6EFB002C54C7 /* long_string_dictionary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = long_string_dictionary.h; sourceTree = "<group>"; }; | |
114 | 118 | D2AAC07E0554694100DB518D /* libBreakpad.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libBreakpad.a; sourceTree = BUILT_PRODUCTS_DIR; }; |
115 | 119 | /* End PBXFileReference section */ |
116 | 120 |
@@ -264,6 +268,8 @@ | ||
264 | 268 | 16C7CC47147D4A4300776EAD /* common */ = { |
265 | 269 | isa = PBXGroup; |
266 | 270 | children = ( |
271 | + CF706DC01F7C6EFB002C54C7 /* long_string_dictionary.h */, | |
272 | + CF6D547C1F9E6FFE00E95174 /* long_string_dictionary.cc */, | |
267 | 273 | 1EEEB60C1720821900F7E689 /* simple_string_dictionary.cc */, |
268 | 274 | 1EEEB60D1720821900F7E689 /* simple_string_dictionary.h */, |
269 | 275 | 16C7CC4A147D4A4300776EAD /* convert_UTF.c */, |
@@ -339,6 +345,7 @@ | ||
339 | 345 | 16C7CEA8147D4A4300776EAD /* string_conversion.h in Headers */, |
340 | 346 | 16BFA67014E195E9009704F8 /* ios_exception_minidump_generator.h in Headers */, |
341 | 347 | 16C92FAD150DF8330053D7BA /* BreakpadController.h in Headers */, |
348 | + CF706DC11F7C6EFB002C54C7 /* long_string_dictionary.h in Headers */, | |
342 | 349 | 1EEEB6101720821900F7E689 /* simple_string_dictionary.h in Headers */, |
343 | 350 | 14569323182CE2C10029C465 /* mach_vm_compat.h in Headers */, |
344 | 351 | ); |
@@ -416,6 +423,7 @@ | ||
416 | 423 | 16C7CDFC147D4A4300776EAD /* minidump_generator.cc in Sources */, |
417 | 424 | 16C7CDFE147D4A4300776EAD /* protected_memory_allocator.cc in Sources */, |
418 | 425 | 16C7CE09147D4A4300776EAD /* uploader.mm in Sources */, |
426 | + CF6D547D1F9E6FFE00E95174 /* long_string_dictionary.cc in Sources */, | |
419 | 427 | 16C7CE19147D4A4300776EAD /* minidump_file_writer.cc in Sources */, |
420 | 428 | 16C7CE40147D4A4300776EAD /* convert_UTF.c in Sources */, |
421 | 429 | 16C7CE79147D4A4300776EAD /* GTMLogger.m in Sources */, |
@@ -121,6 +121,8 @@ | ||
121 | 121 | 'linux/safe_readlink.h', |
122 | 122 | 'linux/synth_elf.cc', |
123 | 123 | 'linux/synth_elf.h', |
124 | + 'long_string_dictionary.cc', | |
125 | + 'long_string_dictionary.h', | |
124 | 126 | 'mac/arch_utilities.cc', |
125 | 127 | 'mac/arch_utilities.h', |
126 | 128 | 'mac/bootstrap_compat.cc', |
@@ -220,6 +222,7 @@ | ||
220 | 222 | 'linux/tests/auto_testfile.h', |
221 | 223 | 'linux/tests/crash_generator.cc', |
222 | 224 | 'linux/tests/crash_generator.h', |
225 | + 'long_string_dictionary_unittest.cc', | |
223 | 226 | 'mac/macho_reader_unittest.cc', |
224 | 227 | 'memory_allocator_unittest.cc', |
225 | 228 | 'memory_range_unittest.cc', |
@@ -0,0 +1,178 @@ | ||
1 | +// Copyright (c) 2017, Google Inc. | |
2 | +// All rights reserved. | |
3 | +// | |
4 | +// Redistribution and use in source and binary forms, with or without | |
5 | +// modification, are permitted provided that the following conditions are | |
6 | +// met: | |
7 | +// | |
8 | +// * Redistributions of source code must retain the above copyright | |
9 | +// notice, this list of conditions and the following disclaimer. | |
10 | +// * Redistributions in binary form must reproduce the above | |
11 | +// copyright notice, this list of conditions and the following disclaimer | |
12 | +// in the documentation and/or other materials provided with the | |
13 | +// distribution. | |
14 | +// * Neither the name of Google Inc. nor the names of its | |
15 | +// contributors may be used to endorse or promote products derived from | |
16 | +// this software without specific prior written permission. | |
17 | +// | |
18 | +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
19 | +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
20 | +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
21 | +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
22 | +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
23 | +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
24 | +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
25 | +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
26 | +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
27 | +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
28 | +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
29 | + | |
30 | +#include "common/long_string_dictionary.h" | |
31 | + | |
32 | +#include <assert.h> | |
33 | +#include <string.h> | |
34 | + | |
35 | +#include <algorithm> | |
36 | +#include <string> | |
37 | + | |
38 | +#include "common/simple_string_dictionary.h" | |
39 | + | |
40 | +#define arraysize(f) (sizeof(f) / sizeof(*f)) | |
41 | + | |
42 | +namespace { | |
43 | +// Suffixes for segment keys. | |
44 | +const char* const kSuffixes[] = {"__1", "__2", "__3", "__4", "__5", "__6", | |
45 | + "__7", "__8", "__9", "__10"}; | |
46 | +#if !defined(NDEBUG) | |
47 | +// The maximum suffix string length. | |
48 | +const size_t kMaxSuffixLength = 4; | |
49 | +#endif | |
50 | +} // namespace | |
51 | + | |
52 | +namespace google_breakpad { | |
53 | + | |
54 | +using std::string; | |
55 | + | |
56 | +void LongStringDictionary::SetKeyValue(const char* key, const char* value) { | |
57 | + assert(key); | |
58 | + if (!key) | |
59 | + return; | |
60 | + | |
61 | + RemoveKey(key); | |
62 | + | |
63 | + if (!value) { | |
64 | + return; | |
65 | + } | |
66 | + | |
67 | + // Key must not be an empty string. | |
68 | + assert(key[0] != '\0'); | |
69 | + if (key[0] == '\0') | |
70 | + return; | |
71 | + | |
72 | + // If the value is not valid for segmentation, forwards the key and the value | |
73 | + // to SetKeyValue of SimpleStringDictionary and returns. | |
74 | + size_t value_length = strlen(value); | |
75 | + if (value_length <= (value_size - 1)) { | |
76 | + SimpleStringDictionary::SetKeyValue(key, value); | |
77 | + return; | |
78 | + } | |
79 | + | |
80 | + size_t key_length = strlen(key); | |
81 | + assert(key_length + kMaxSuffixLength <= (key_size - 1)); | |
82 | + | |
83 | + char segment_key[key_size]; | |
84 | + char segment_value[value_size]; | |
85 | + | |
86 | + strcpy(segment_key, key); | |
87 | + | |
88 | + const char* remain_value = value; | |
89 | + size_t remain_value_length = strlen(value); | |
90 | + | |
91 | + for (unsigned long i = 0; i < arraysize(kSuffixes); i++) { | |
92 | + if (remain_value_length == 0) { | |
93 | + return; | |
94 | + } | |
95 | + | |
96 | + strcpy(segment_key + key_length, kSuffixes[i]); | |
97 | + | |
98 | + size_t segment_value_length = | |
99 | + std::min(remain_value_length, value_size - 1); | |
100 | + | |
101 | + strncpy(segment_value, remain_value, segment_value_length); | |
102 | + segment_value[segment_value_length] = '\0'; | |
103 | + | |
104 | + remain_value += segment_value_length; | |
105 | + remain_value_length -= segment_value_length; | |
106 | + | |
107 | + SimpleStringDictionary::SetKeyValue(segment_key, segment_value); | |
108 | + } | |
109 | +} | |
110 | + | |
111 | +bool LongStringDictionary::RemoveKey(const char* key) { | |
112 | + assert(key); | |
113 | + if (!key) | |
114 | + return false; | |
115 | + | |
116 | + if (SimpleStringDictionary::RemoveKey(key)) { | |
117 | + return true; | |
118 | + } | |
119 | + | |
120 | + size_t key_length = strlen(key); | |
121 | + assert(key_length + kMaxSuffixLength <= (key_size - 1)); | |
122 | + | |
123 | + char segment_key[key_size]; | |
124 | + strcpy(segment_key, key); | |
125 | + | |
126 | + unsigned long i = 0; | |
127 | + for (; i < arraysize(kSuffixes); i++) { | |
128 | + strcpy(segment_key + key_length, kSuffixes[i]); | |
129 | + if (!SimpleStringDictionary::RemoveKey(segment_key)) { | |
130 | + break; | |
131 | + } | |
132 | + } | |
133 | + return i != 0; | |
134 | +} | |
135 | + | |
136 | +const string LongStringDictionary::GetValueForKey(const char* key) const { | |
137 | + assert(key); | |
138 | + if (!key) | |
139 | + return ""; | |
140 | + | |
141 | + // Key must not be an empty string. | |
142 | + assert(key[0] != '\0'); | |
143 | + if (key[0] == '\0') | |
144 | + return ""; | |
145 | + | |
146 | + const char* value = SimpleStringDictionary::GetValueForKey(key); | |
147 | + if (value) | |
148 | + return string(value); | |
149 | + | |
150 | + size_t key_length = strlen(key); | |
151 | + assert(key_length + kMaxSuffixLength <= (key_size - 1)); | |
152 | + | |
153 | + bool found_segment = false; | |
154 | + char segment_key[key_size]; | |
155 | + string return_value; | |
156 | + | |
157 | + strcpy(segment_key, key); | |
158 | + for (unsigned long i = 0; i < arraysize(kSuffixes); i++) { | |
159 | + strcpy(segment_key + key_length, kSuffixes[i]); | |
160 | + | |
161 | + const char* segment_value = | |
162 | + SimpleStringDictionary::GetValueForKey(segment_key); | |
163 | + | |
164 | + if (segment_value != NULL) { | |
165 | + found_segment = true; | |
166 | + return_value.append(segment_value); | |
167 | + } else { | |
168 | + break; | |
169 | + } | |
170 | + } | |
171 | + | |
172 | + if (found_segment) { | |
173 | + return return_value; | |
174 | + } | |
175 | + return ""; | |
176 | +} | |
177 | + | |
178 | +} // namespace google_breakpad |
@@ -0,0 +1,87 @@ | ||
1 | +// Copyright (c) 2017, Google Inc. | |
2 | +// All rights reserved. | |
3 | +// | |
4 | +// Redistribution and use in source and binary forms, with or without | |
5 | +// modification, are permitted provided that the following conditions are | |
6 | +// met: | |
7 | +// | |
8 | +// * Redistributions of source code must retain the above copyright | |
9 | +// notice, this list of conditions and the following disclaimer. | |
10 | +// * Redistributions in binary form must reproduce the above | |
11 | +// copyright notice, this list of conditions and the following disclaimer | |
12 | +// in the documentation and/or other materials provided with the | |
13 | +// distribution. | |
14 | +// * Neither the name of Google Inc. nor the names of its | |
15 | +// contributors may be used to endorse or promote products derived from | |
16 | +// this software without specific prior written permission. | |
17 | +// | |
18 | +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
19 | +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
20 | +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
21 | +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
22 | +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
23 | +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
24 | +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
25 | +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
26 | +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
27 | +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
28 | +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
29 | + | |
30 | +#ifndef COMMON_LONG_STRING_DICTIONARY_H_ | |
31 | +#define COMMON_LONG_STRING_DICTIONARY_H_ | |
32 | + | |
33 | +#include <string> | |
34 | + | |
35 | +#include "common/simple_string_dictionary.h" | |
36 | + | |
37 | +namespace google_breakpad { | |
38 | +// key_size is the maxium size that |key| can take in | |
39 | +// SimpleStringDictionary which is defined in simple_string_dictionary.h. | |
40 | +// | |
41 | +// value_size is the maxium size that |value| can take in | |
42 | +// SimpleStringDictionary which is defined in simple_string_dictionary.h. | |
43 | +// | |
44 | +// LongStringDictionary is a subclass of SimpleStringDictionary which supports | |
45 | +// longer values to be stored in the dictionary. The maximum length supported is | |
46 | +// (value_size - 1) * 10. | |
47 | +// | |
48 | +// For example, LongStringDictionary will store long value with key 'abc' into | |
49 | +// segment values with segment keys 'abc__1', 'abc__2', 'abc__3', ... | |
50 | +// | |
51 | +// Clients must avoid using the same suffixes as their key's suffix when | |
52 | +// LongStringDictionary is used. | |
53 | +class LongStringDictionary : public SimpleStringDictionary { | |
54 | + public: | |
55 | + // Stores |value| into |key|, or segment values into segment keys. The maxium | |
56 | + // number of segments is 10. If |value| can not be stored in 10 segments, it | |
57 | + // will be truncated. Replacing the existing value if |key| is already present | |
58 | + // and replacing the existing segment values if segment keys are already | |
59 | + // present. | |
60 | + // | |
61 | + // |key| must not be NULL. If the |value| need to be divided into segments, | |
62 | + // the lengh of |key| must be smaller enough so that lengths of segment keys | |
63 | + // which are key with suffixes are all samller than (key_size - 1). Currently, | |
64 | + // the max length of suffixes are 4. | |
65 | + // | |
66 | + // If |value| is NULL, the key and its corresponding segment keys are removed | |
67 | + // from the map. If there is no more space in the map, then the operation | |
68 | + // silently fails. | |
69 | + void SetKeyValue(const char* key, const char* value); | |
70 | + | |
71 | + // Given |key|, removes any associated value or associated segment values. | |
72 | + // |key| must not be NULL. If the key is not found, searchs its segment keys | |
73 | + // and removes corresponding segment values if found. | |
74 | + bool RemoveKey(const char* key); | |
75 | + | |
76 | + // Given |key|, returns its corresponding |value|. |key| must not be NULL. If | |
77 | + // the key is found, its corresponding |value| is returned. | |
78 | + // | |
79 | + // If no corresponding |value| is found, segment keys of the given |key| will | |
80 | + // be used to search for corresponding segment values. If segment values | |
81 | + // exist, assembled value from them is returned. If no segment value exists, | |
82 | + // NULL is returned. | |
83 | + const std::string GetValueForKey(const char* key) const; | |
84 | +}; | |
85 | +} // namespace google_breakpad | |
86 | + | |
87 | +#endif // COMMON_LONG_STRING_DICTIONARY_H_ |
@@ -0,0 +1,301 @@ | ||
1 | +// Copyright (c) 2017, Google Inc. | |
2 | +// All rights reserved. | |
3 | +// | |
4 | +// Redistribution and use in source and binary forms, with or without | |
5 | +// modification, are permitted provided that the following conditions are | |
6 | +// met: | |
7 | +// | |
8 | +// * Redistributions of source code must retain the above copyright | |
9 | +// notice, this list of conditions and the following disclaimer. | |
10 | +// * Redistributions in binary form must reproduce the above | |
11 | +// copyright notice, this list of conditions and the following disclaimer | |
12 | +// in the documentation and/or other materials provided with the | |
13 | +// distribution. | |
14 | +// * Neither the name of Google Inc. nor the names of its | |
15 | +// contributors may be used to endorse or promote products derived from | |
16 | +// this software without specific prior written permission. | |
17 | +// | |
18 | +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
19 | +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
20 | +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
21 | +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
22 | +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
23 | +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
24 | +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
25 | +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
26 | +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
27 | +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
28 | +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
29 | + | |
30 | +#include <algorithm> | |
31 | +#include <string> | |
32 | + | |
33 | +#include "breakpad_googletest_includes.h" | |
34 | +#include "common/long_string_dictionary.h" | |
35 | + | |
36 | +namespace google_breakpad { | |
37 | + | |
38 | +using std::string; | |
39 | + | |
40 | +TEST(LongStringDictionary, LongStringDictionary) { | |
41 | + // Make a new dictionary | |
42 | + LongStringDictionary dict; | |
43 | + | |
44 | + // Set three distinct values on three keys | |
45 | + dict.SetKeyValue("key1", "value1"); | |
46 | + dict.SetKeyValue("key2", "value2"); | |
47 | + dict.SetKeyValue("key3", "value3"); | |
48 | + | |
49 | + EXPECT_EQ("value1", dict.GetValueForKey("key1")); | |
50 | + EXPECT_EQ("value2", dict.GetValueForKey("key2")); | |
51 | + EXPECT_EQ("value3", dict.GetValueForKey("key3")); | |
52 | + EXPECT_EQ(3u, dict.GetCount()); | |
53 | + // try an unknown key | |
54 | + EXPECT_EQ("", dict.GetValueForKey("key4")); | |
55 | + | |
56 | + // Remove a key | |
57 | + dict.RemoveKey("key3"); | |
58 | + | |
59 | + // Now make sure it's not there anymore | |
60 | + EXPECT_EQ("", dict.GetValueForKey("key3")); | |
61 | + | |
62 | + // Remove by setting value to NULL | |
63 | + dict.SetKeyValue("key2", NULL); | |
64 | + | |
65 | + // Now make sure it's not there anymore | |
66 | + EXPECT_EQ("", dict.GetValueForKey("key2")); | |
67 | +} | |
68 | + | |
69 | +// Add a bunch of values to the dictionary, remove some entries in the middle, | |
70 | +// and then add more. | |
71 | +TEST(LongStringDictionary, Iterator) { | |
72 | + LongStringDictionary* dict = new LongStringDictionary(); | |
73 | + ASSERT_TRUE(dict); | |
74 | + | |
75 | + char key[LongStringDictionary::key_size]; | |
76 | + char value[LongStringDictionary::value_size]; | |
77 | + | |
78 | + const int kDictionaryCapacity = LongStringDictionary::num_entries; | |
79 | + const int kPartitionIndex = kDictionaryCapacity - 5; | |
80 | + | |
81 | + // We assume at least this size in the tests below | |
82 | + ASSERT_GE(kDictionaryCapacity, 64); | |
83 | + | |
84 | + // We'll keep track of the number of key/value pairs we think should | |
85 | + // be in the dictionary | |
86 | + int expectedDictionarySize = 0; | |
87 | + | |
88 | + // Set a bunch of key/value pairs like key0/value0, key1/value1, ... | |
89 | + for (int i = 0; i < kPartitionIndex; ++i) { | |
90 | + sprintf(key, "key%d", i); | |
91 | + sprintf(value, "value%d", i); | |
92 | + dict->SetKeyValue(key, value); | |
93 | + } | |
94 | + expectedDictionarySize = kPartitionIndex; | |
95 | + | |
96 | + // set a couple of the keys twice (with the same value) - should be nop | |
97 | + dict->SetKeyValue("key2", "value2"); | |
98 | + dict->SetKeyValue("key4", "value4"); | |
99 | + dict->SetKeyValue("key15", "value15"); | |
100 | + | |
101 | + // Remove some random elements in the middle | |
102 | + dict->RemoveKey("key7"); | |
103 | + dict->RemoveKey("key18"); | |
104 | + dict->RemoveKey("key23"); | |
105 | + dict->RemoveKey("key31"); | |
106 | + expectedDictionarySize -= 4; // we just removed four key/value pairs | |
107 | + | |
108 | + // Set some more key/value pairs like key59/value59, key60/value60, ... | |
109 | + for (int i = kPartitionIndex; i < kDictionaryCapacity; ++i) { | |
110 | + sprintf(key, "key%d", i); | |
111 | + sprintf(value, "value%d", i); | |
112 | + dict->SetKeyValue(key, value); | |
113 | + } | |
114 | + expectedDictionarySize += kDictionaryCapacity - kPartitionIndex; | |
115 | + | |
116 | + // Now create an iterator on the dictionary | |
117 | + SimpleStringDictionary::Iterator iter(*dict); | |
118 | + | |
119 | + // We then verify that it iterates through exactly the number of | |
120 | + // key/value pairs we expect, and that they match one-for-one with what we | |
121 | + // would expect. The ordering of the iteration does not matter... | |
122 | + | |
123 | + // used to keep track of number of occurrences found for key/value pairs | |
124 | + int count[kDictionaryCapacity]; | |
125 | + memset(count, 0, sizeof(count)); | |
126 | + | |
127 | + int totalCount = 0; | |
128 | + | |
129 | + const SimpleStringDictionary::Entry* entry; | |
130 | + while ((entry = iter.Next())) { | |
131 | + totalCount++; | |
132 | + | |
133 | + // Extract keyNumber from a string of the form key<keyNumber> | |
134 | + int keyNumber; | |
135 | + sscanf(entry->key, "key%d", &keyNumber); | |
136 | + | |
137 | + // Extract valueNumber from a string of the form value<valueNumber> | |
138 | + int valueNumber; | |
139 | + sscanf(entry->value, "value%d", &valueNumber); | |
140 | + | |
141 | + // The value number should equal the key number since that's how we set them | |
142 | + EXPECT_EQ(keyNumber, valueNumber); | |
143 | + | |
144 | + // Key and value numbers should be in proper range: | |
145 | + // 0 <= keyNumber < kDictionaryCapacity | |
146 | + bool isKeyInGoodRange = (keyNumber >= 0 && keyNumber < kDictionaryCapacity); | |
147 | + bool isValueInGoodRange = | |
148 | + (valueNumber >= 0 && valueNumber < kDictionaryCapacity); | |
149 | + EXPECT_TRUE(isKeyInGoodRange); | |
150 | + EXPECT_TRUE(isValueInGoodRange); | |
151 | + | |
152 | + if (isKeyInGoodRange && isValueInGoodRange) { | |
153 | + ++count[keyNumber]; | |
154 | + } | |
155 | + } | |
156 | + | |
157 | + // Make sure each of the key/value pairs showed up exactly one time, except | |
158 | + // for the ones which we removed. | |
159 | + for (size_t i = 0; i < kDictionaryCapacity; ++i) { | |
160 | + // Skip over key7, key18, key23, and key31, since we removed them | |
161 | + if (!(i == 7 || i == 18 || i == 23 || i == 31)) { | |
162 | + EXPECT_EQ(count[i], 1); | |
163 | + } | |
164 | + } | |
165 | + | |
166 | + // Make sure the number of iterations matches the expected dictionary size. | |
167 | + EXPECT_EQ(totalCount, expectedDictionarySize); | |
168 | +} | |
169 | + | |
170 | +TEST(LongStringDictionary, AddRemove) { | |
171 | + LongStringDictionary dict; | |
172 | + dict.SetKeyValue("rob", "ert"); | |
173 | + dict.SetKeyValue("mike", "pink"); | |
174 | + dict.SetKeyValue("mark", "allays"); | |
175 | + | |
176 | + EXPECT_EQ(3u, dict.GetCount()); | |
177 | + EXPECT_EQ("ert", dict.GetValueForKey("rob")); | |
178 | + EXPECT_EQ("pink", dict.GetValueForKey("mike")); | |
179 | + EXPECT_EQ("allays", dict.GetValueForKey("mark")); | |
180 | + | |
181 | + dict.RemoveKey("mike"); | |
182 | + | |
183 | + EXPECT_EQ(2u, dict.GetCount()); | |
184 | + EXPECT_EQ("", dict.GetValueForKey("mike")); | |
185 | + | |
186 | + dict.SetKeyValue("mark", "mal"); | |
187 | + EXPECT_EQ(2u, dict.GetCount()); | |
188 | + EXPECT_EQ("mal", dict.GetValueForKey("mark")); | |
189 | + | |
190 | + dict.RemoveKey("mark"); | |
191 | + EXPECT_EQ(1u, dict.GetCount()); | |
192 | + EXPECT_EQ("", dict.GetValueForKey("mark")); | |
193 | +} | |
194 | + | |
195 | +TEST(LongStringDictionary, AddRemoveLongValue) { | |
196 | + LongStringDictionary dict; | |
197 | + | |
198 | + string long_value = string(256, 'x'); | |
199 | + dict.SetKeyValue("rob", long_value.c_str()); | |
200 | + | |
201 | + EXPECT_EQ(2u, dict.GetCount()); | |
202 | + | |
203 | + string long_value_part_1 = string(255, 'x'); | |
204 | + | |
205 | + EXPECT_EQ(long_value_part_1, dict.GetValueForKey("rob__1")); | |
206 | + EXPECT_EQ("x", dict.GetValueForKey("rob__2")); | |
207 | + | |
208 | + EXPECT_EQ(long_value, dict.GetValueForKey("rob")); | |
209 | + | |
210 | + dict.RemoveKey("rob"); | |
211 | + EXPECT_EQ(0u, dict.GetCount()); | |
212 | +} | |
213 | + | |
214 | +TEST(LongStringDictionary, AddRemoveSuperLongValue) { | |
215 | + LongStringDictionary dict; | |
216 | + | |
217 | + string long_value = string(255 * 10, 'x'); | |
218 | + dict.SetKeyValue("rob", long_value.c_str()); | |
219 | + | |
220 | + EXPECT_EQ(10u, dict.GetCount()); | |
221 | + | |
222 | + string long_value_part = string(255, 'x'); | |
223 | + | |
224 | + EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__1")); | |
225 | + EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__2")); | |
226 | + EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__3")); | |
227 | + EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__4")); | |
228 | + EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__5")); | |
229 | + EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__6")); | |
230 | + EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__7")); | |
231 | + EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__8")); | |
232 | + EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__9")); | |
233 | + EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__10")); | |
234 | + EXPECT_EQ(10u, dict.GetCount()); | |
235 | + | |
236 | + EXPECT_EQ(long_value, dict.GetValueForKey("rob")); | |
237 | + | |
238 | + dict.RemoveKey("rob"); | |
239 | + EXPECT_EQ(0u, dict.GetCount()); | |
240 | +} | |
241 | + | |
242 | +TEST(LongStringDictionary, TruncateSuperLongValue) { | |
243 | + LongStringDictionary dict; | |
244 | + | |
245 | + string long_value = string(255 * 11, 'x'); | |
246 | + dict.SetKeyValue("rob", long_value.c_str()); | |
247 | + | |
248 | + EXPECT_EQ(10u, dict.GetCount()); | |
249 | + | |
250 | + string long_value_part = string(255, 'x'); | |
251 | + | |
252 | + EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__1")); | |
253 | + EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__2")); | |
254 | + EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__3")); | |
255 | + EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__4")); | |
256 | + EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__5")); | |
257 | + EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__6")); | |
258 | + EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__7")); | |
259 | + EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__8")); | |
260 | + EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__9")); | |
261 | + EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__10")); | |
262 | + EXPECT_EQ(10u, dict.GetCount()); | |
263 | + | |
264 | + string expected_long_value = string(255 * 10, 'x'); | |
265 | + EXPECT_EQ(expected_long_value, dict.GetValueForKey("rob")); | |
266 | + | |
267 | + dict.RemoveKey("rob"); | |
268 | + EXPECT_EQ(0u, dict.GetCount()); | |
269 | +} | |
270 | + | |
271 | +TEST(LongStringDictionary, OverrideLongValue) { | |
272 | + LongStringDictionary dict; | |
273 | + | |
274 | + string long_value = string(255 * 10, 'x'); | |
275 | + dict.SetKeyValue("rob", long_value.c_str()); | |
276 | + | |
277 | + EXPECT_EQ(10u, dict.GetCount()); | |
278 | + EXPECT_EQ(long_value, dict.GetValueForKey("rob")); | |
279 | + | |
280 | + dict.SetKeyValue("rob", "short_value"); | |
281 | + | |
282 | + EXPECT_EQ(1u, dict.GetCount()); | |
283 | + EXPECT_EQ("short_value", dict.GetValueForKey("rob")); | |
284 | +} | |
285 | + | |
286 | +TEST(LongStringDictionary, OverrideShortValue) { | |
287 | + LongStringDictionary dict; | |
288 | + | |
289 | + dict.SetKeyValue("rob", "short_value"); | |
290 | + | |
291 | + EXPECT_EQ(1u, dict.GetCount()); | |
292 | + EXPECT_EQ("short_value", dict.GetValueForKey("rob")); | |
293 | + | |
294 | + string long_value = string(255 * 10, 'x'); | |
295 | + dict.SetKeyValue("rob", long_value.c_str()); | |
296 | + | |
297 | + EXPECT_EQ(10u, dict.GetCount()); | |
298 | + EXPECT_EQ(long_value, dict.GetValueForKey("rob")); | |
299 | +} | |
300 | + | |
301 | +} // namespace google_breakpad |
@@ -209,20 +209,22 @@ class NonAllocatingMap { | ||
209 | 209 | |
210 | 210 | // Given |key|, removes any associated value. |key| must not be NULL. If |
211 | 211 | // the key is not found, this is a noop. |
212 | - void RemoveKey(const char* key) { | |
212 | + bool RemoveKey(const char* key) { | |
213 | 213 | assert(key); |
214 | 214 | if (!key) |
215 | - return; | |
215 | + return false; | |
216 | 216 | |
217 | 217 | Entry* entry = GetEntryForKey(key); |
218 | 218 | if (entry) { |
219 | 219 | entry->key[0] = '\0'; |
220 | 220 | entry->value[0] = '\0'; |
221 | + return true; | |
221 | 222 | } |
222 | 223 | |
223 | 224 | #ifndef NDEBUG |
224 | 225 | assert(GetEntryForKey(key) == NULL); |
225 | 226 | #endif |
227 | + return false; | |
226 | 228 | } |
227 | 229 | |
228 | 230 | // Places a serialized version of the map into |map| and returns the size. |