OLD | NEW |
1 // Copyright 2015 The Crashpad Authors. All rights reserved. | 1 // Copyright 2015 The Crashpad Authors. All rights reserved. |
2 // | 2 // |
3 // Licensed under the Apache License, Version 2.0 (the "License"); | 3 // Licensed under the Apache License, Version 2.0 (the "License"); |
4 // you may not use this file except in compliance with the License. | 4 // you may not use this file except in compliance with the License. |
5 // You may obtain a copy of the License at | 5 // You may obtain a copy of the License at |
6 // | 6 // |
7 // http://www.apache.org/licenses/LICENSE-2.0 | 7 // http://www.apache.org/licenses/LICENSE-2.0 |
8 // | 8 // |
9 // Unless required by applicable law or agreed to in writing, software | 9 // Unless required by applicable law or agreed to in writing, software |
10 // distributed under the License is distributed on an "AS IS" BASIS, | 10 // distributed under the License is distributed on an "AS IS" BASIS, |
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 // See the License for the specific language governing permissions and | 12 // See the License for the specific language governing permissions and |
13 // limitations under the License. | 13 // limitations under the License. |
14 | 14 |
15 #include <errno.h> | 15 #include <errno.h> |
16 #include <getopt.h> | 16 #include <getopt.h> |
17 #include <libgen.h> | |
18 #include <stdio.h> | 17 #include <stdio.h> |
19 #include <stdlib.h> | 18 #include <stdlib.h> |
20 #include <string.h> | 19 #include <string.h> |
21 #include <sys/types.h> | 20 #include <sys/types.h> |
22 #include <time.h> | 21 #include <time.h> |
23 | 22 |
24 #include <string> | 23 #include <string> |
25 #include <vector> | 24 #include <vector> |
26 | 25 |
27 #include "base/basictypes.h" | 26 #include "base/basictypes.h" |
| 27 #include "base/files/file_path.h" |
28 #include "base/logging.h" | 28 #include "base/logging.h" |
29 #include "base/files/file_path.h" | |
30 #include "base/memory/scoped_ptr.h" | 29 #include "base/memory/scoped_ptr.h" |
31 #include "base/numerics/safe_conversions.h" | 30 #include "base/numerics/safe_conversions.h" |
| 31 #include "base/strings/string_util.h" |
| 32 #include "base/strings/utf_string_conversions.h" |
| 33 #include "build/build_config.h" |
32 #include "client/crash_report_database.h" | 34 #include "client/crash_report_database.h" |
33 #include "client/settings.h" | 35 #include "client/settings.h" |
34 #include "tools/tool_support.h" | 36 #include "tools/tool_support.h" |
35 #include "util/file/file_io.h" | 37 #include "util/file/file_io.h" |
36 #include "util/file/file_reader.h" | 38 #include "util/file/file_reader.h" |
37 #include "util/misc/uuid.h" | 39 #include "util/misc/uuid.h" |
38 | 40 |
| 41 #if defined(OS_POSIX) |
| 42 base::FilePath::StringType UTF8ToFilePathStringType(const char* path) { |
| 43 return path; |
| 44 } |
| 45 #elif defined(OS_WIN) |
| 46 base::FilePath::StringType UTF8ToFilePathStringType(const char* path) { |
| 47 return base::UTF8ToUTF16(path); |
| 48 } |
| 49 #endif |
| 50 |
39 namespace crashpad { | 51 namespace crashpad { |
40 namespace { | 52 namespace { |
41 | 53 |
42 void Usage(const std::string& me) { | 54 void Usage(const base::FilePath& me) { |
43 fprintf(stderr, | 55 fprintf(stderr, |
44 "Usage: %s [OPTION]... PID\n" | 56 "Usage: %" PRFilePath " [OPTION]... PID\n" |
45 "Operate on Crashpad crash report databases.\n" | 57 "Operate on Crashpad crash report databases.\n" |
46 "\n" | 58 "\n" |
47 " -d, --database=PATH operate on the crash report database at PATH\
n" | 59 " -d, --database=PATH operate on the crash report database at PATH\
n" |
48 " --show-client-id show the client ID\n" | 60 " --show-client-id show the client ID\n" |
49 " --show-uploads-enabled show whether uploads are enabled\n" | 61 " --show-uploads-enabled show whether uploads are enabled\n" |
50 " --show-last-upload-attempt-time\n" | 62 " --show-last-upload-attempt-time\n" |
51 " show the last-upload-attempt time\n" | 63 " show the last-upload-attempt time\n" |
52 " --show-pending-reports show reports eligible for upload\n" | 64 " --show-pending-reports show reports eligible for upload\n" |
53 " --show-completed-reports show reports not eligible for upload\n" | 65 " --show-completed-reports show reports not eligible for upload\n" |
54 " --show-all-report-info with --show-*-reports, show more information\
n" | 66 " --show-all-report-info with --show-*-reports, show more information\
n" |
55 " --show-report=UUID show report stored under UUID\n" | 67 " --show-report=UUID show report stored under UUID\n" |
56 " --set-uploads-enabled=BOOL enable or disable uploads\n" | 68 " --set-uploads-enabled=BOOL enable or disable uploads\n" |
57 " --set-last-upload-attempt-time=TIME\n" | 69 " --set-last-upload-attempt-time=TIME\n" |
58 " set the last-upload-attempt time to TIME\n" | 70 " set the last-upload-attempt time to TIME\n" |
59 " --new-report=PATH submit a new report at PATH\n" | 71 " --new-report=PATH submit a new report at PATH\n" |
60 " --utc show and set UTC times instead of local\n" | 72 " --utc show and set UTC times instead of local\n" |
61 " --help display this help and exit\n" | 73 " --help display this help and exit\n" |
62 " --version output version information and exit\n", | 74 " --version output version information and exit\n", |
63 me.c_str()); | 75 me.value().c_str()); |
64 ToolSupport::UsageTail(me); | 76 ToolSupport::UsageTail(me); |
65 } | 77 } |
66 | 78 |
67 struct Options { | 79 struct Options { |
68 std::vector<UUID> show_reports; | 80 std::vector<UUID> show_reports; |
69 std::vector<base::FilePath> new_report_paths; | 81 std::vector<base::FilePath> new_report_paths; |
70 const char* database; | 82 const char* database; |
71 const char* set_last_upload_attempt_time_string; | 83 const char* set_last_upload_attempt_time_string; |
72 time_t set_last_upload_attempt_time; | 84 time_t set_last_upload_attempt_time; |
73 bool show_client_id; | 85 bool show_client_id; |
(...skipping 23 matching lines...) Expand all Loading... |
97 const char* const kTrueWords[] = { | 109 const char* const kTrueWords[] = { |
98 "1", | 110 "1", |
99 "true", | 111 "true", |
100 "yes", | 112 "yes", |
101 "on", | 113 "on", |
102 "enabled", | 114 "enabled", |
103 "set", | 115 "set", |
104 }; | 116 }; |
105 | 117 |
106 for (size_t index = 0; index < arraysize(kFalseWords); ++index) { | 118 for (size_t index = 0; index < arraysize(kFalseWords); ++index) { |
107 if (strcasecmp(string, kFalseWords[index]) == 0) { | 119 if (base::strcasecmp(string, kFalseWords[index]) == 0) { |
108 *boolean = false; | 120 *boolean = false; |
109 return true; | 121 return true; |
110 } | 122 } |
111 } | 123 } |
112 | 124 |
113 for (size_t index = 0; index < arraysize(kTrueWords); ++index) { | 125 for (size_t index = 0; index < arraysize(kTrueWords); ++index) { |
114 if (strcasecmp(string, kTrueWords[index]) == 0) { | 126 if (base::strcasecmp(string, kTrueWords[index]) == 0) { |
115 *boolean = true; | 127 *boolean = true; |
116 return true; | 128 return true; |
117 } | 129 } |
118 } | 130 } |
119 | 131 |
120 return false; | 132 return false; |
121 } | 133 } |
122 | 134 |
123 // Converts |boolean| to a string, either "true" or "false". | 135 // Converts |boolean| to a string, either "true" or "false". |
124 std::string BoolToString(bool boolean) { | 136 std::string BoolToString(bool boolean) { |
125 return std::string(boolean ? "true" : "false"); | 137 return std::string(boolean ? "true" : "false"); |
126 } | 138 } |
127 | 139 |
128 // Converts |string| to |time|, returning true if a conversion could be | 140 // Converts |string| to |time|, returning true if a conversion could be |
129 // performed, and false without setting |boolean| if no conversion could be | 141 // performed, and false without setting |boolean| if no conversion could be |
130 // performed. Various time formats are recognized, including several string | 142 // performed. Various time formats are recognized, including several string |
131 // representations and a numeric time_t representation. The special string | 143 // representations and a numeric time_t representation. The special string |
132 // "never" is recognized as |string| and converts to a |time| value of 0. |utc|, | 144 // "never" is recognized as |string| and converts to a |time| value of 0. |utc|, |
133 // when true, causes |string| to be interpreted as a UTC time rather than a | 145 // when true, causes |string| to be interpreted as a UTC time rather than a |
134 // local time when the time zone is ambiguous. | 146 // local time when the time zone is ambiguous. |
135 bool StringToTime(const char* string, time_t* time, bool utc) { | 147 bool StringToTime(const char* string, time_t* time, bool utc) { |
136 if (strcasecmp(string, "never") == 0) { | 148 if (base::strcasecmp(string, "never") == 0) { |
137 *time = 0; | 149 *time = 0; |
138 return true; | 150 return true; |
139 } | 151 } |
140 | 152 |
141 const char* end = string + strlen(string); | 153 const char* end = string + strlen(string); |
142 | 154 |
143 const char* const kFormats[] = { | 155 const char* const kFormats[] = { |
144 "%Y-%m-%d %H:%M:%S %Z", | 156 "%Y-%m-%d %H:%M:%S %Z", |
145 "%Y-%m-%d %H:%M:%S", | 157 "%Y-%m-%d %H:%M:%S", |
146 "%+", | 158 "%+", |
147 }; | 159 }; |
148 | 160 |
149 for (size_t index = 0; index < arraysize(kFormats); ++index) { | 161 for (size_t index = 0; index < arraysize(kFormats); ++index) { |
150 tm time_tm; | 162 tm time_tm; |
151 const char* strptime_result = strptime(string, kFormats[index], &time_tm); | 163 const char* strptime_result = strptime(string, kFormats[index], &time_tm); |
152 if (strptime_result == end) { | 164 if (strptime_result == end) { |
153 if (utc) { | 165 if (utc) { |
154 *time = timegm(&time_tm); | 166 *time = timegm(&time_tm); |
155 } else { | 167 } else { |
156 *time = timelocal(&time_tm); | 168 *time = mktime(&time_tm); |
157 } | 169 } |
158 | 170 |
159 return true; | 171 return true; |
160 } | 172 } |
161 } | 173 } |
162 | 174 |
163 char* end_result; | 175 char* end_result; |
164 errno = 0; | 176 errno = 0; |
165 long long strtoll_result = strtoll(string, &end_result, 0); | 177 long long strtoll_result = strtoll(string, &end_result, 0); |
166 if (end_result == end && errno == 0 && | 178 if (end_result == end && errno == 0 && |
167 base::IsValueInRangeForNumericType<time_t>(strtoll_result)) { | 179 base::IsValueInRangeForNumericType<time_t>(strtoll_result)) { |
168 *time = strtoll_result; | 180 *time = strtoll_result; |
169 return true; | 181 return true; |
170 } | 182 } |
171 | 183 |
172 return false; | 184 return false; |
173 } | 185 } |
174 | 186 |
175 // Converst |time_tt| to a string, and returns it. |utc| determines whether the | 187 // Converts |time_tt| to a string, and returns it. |utc| determines whether the |
176 // converted time will reference local time or UTC. If |time_tt| is 0, the | 188 // converted time will reference local time or UTC. If |time_tt| is 0, the |
177 // string "never" will be returned as a special case. | 189 // string "never" will be returned as a special case. |
178 std::string TimeToString(time_t time_tt, bool utc) { | 190 std::string TimeToString(time_t time_tt, bool utc) { |
179 if (time_tt == 0) { | 191 if (time_tt == 0) { |
180 return std::string("never"); | 192 return std::string("never"); |
181 } | 193 } |
182 | 194 |
183 tm time_tm; | 195 tm time_tm; |
184 if (utc) { | 196 if (utc) { |
185 gmtime_r(&time_tt, &time_tm); | 197 gmtime_r(&time_tt, &time_tm); |
(...skipping 10 matching lines...) Expand all Loading... |
196 } | 208 } |
197 | 209 |
198 // Shows information about a single |report|. |space_count| is the number of | 210 // Shows information about a single |report|. |space_count| is the number of |
199 // spaces to print before each line that is printed. |utc| determines whether | 211 // spaces to print before each line that is printed. |utc| determines whether |
200 // times should be shown in UTC or the local time zone. | 212 // times should be shown in UTC or the local time zone. |
201 void ShowReport(const CrashReportDatabase::Report& report, | 213 void ShowReport(const CrashReportDatabase::Report& report, |
202 size_t space_count, | 214 size_t space_count, |
203 bool utc) { | 215 bool utc) { |
204 std::string spaces(space_count, ' '); | 216 std::string spaces(space_count, ' '); |
205 | 217 |
206 printf("%sPath: %s\n", spaces.c_str(), report.file_path.value().c_str()); | 218 printf("%sPath: %" PRFilePath "\n", |
| 219 spaces.c_str(), |
| 220 report.file_path.value().c_str()); |
207 if (!report.id.empty()) { | 221 if (!report.id.empty()) { |
208 printf("%sRemote ID: %s\n", spaces.c_str(), report.id.c_str()); | 222 printf("%sRemote ID: %s\n", spaces.c_str(), report.id.c_str()); |
209 } | 223 } |
210 printf("%sCreation time: %s\n", | 224 printf("%sCreation time: %s\n", |
211 spaces.c_str(), | 225 spaces.c_str(), |
212 TimeToString(report.creation_time, utc).c_str()); | 226 TimeToString(report.creation_time, utc).c_str()); |
213 printf("%sUploaded: %s\n", | 227 printf("%sUploaded: %s\n", |
214 spaces.c_str(), | 228 spaces.c_str(), |
215 BoolToString(report.uploaded).c_str()); | 229 BoolToString(report.uploaded).c_str()); |
216 printf("%sLast upload attempt time: %s\n", | 230 printf("%sLast upload attempt time: %s\n", |
(...skipping 15 matching lines...) Expand all Loading... |
232 | 246 |
233 for (const CrashReportDatabase::Report& report : reports) { | 247 for (const CrashReportDatabase::Report& report : reports) { |
234 printf("%s%s%s\n", spaces.c_str(), report.uuid.ToString().c_str(), colon); | 248 printf("%s%s%s\n", spaces.c_str(), report.uuid.ToString().c_str(), colon); |
235 if (options.show_all_report_info) { | 249 if (options.show_all_report_info) { |
236 ShowReport(report, space_count + 2, options.utc); | 250 ShowReport(report, space_count + 2, options.utc); |
237 } | 251 } |
238 } | 252 } |
239 } | 253 } |
240 | 254 |
241 int DatabaseUtilMain(int argc, char* argv[]) { | 255 int DatabaseUtilMain(int argc, char* argv[]) { |
242 const std::string me(basename(argv[0])); | 256 const base::FilePath me( |
| 257 base::FilePath(UTF8ToFilePathStringType(argv[0])).BaseName()); |
243 | 258 |
244 enum OptionFlags { | 259 enum OptionFlags { |
245 // “Short” (single-character) options. | 260 // “Short” (single-character) options. |
246 kOptionDatabase = 'd', | 261 kOptionDatabase = 'd', |
247 | 262 |
248 // Long options without short equivalents. | 263 // Long options without short equivalents. |
249 kOptionLastChar = 255, | 264 kOptionLastChar = 255, |
250 kOptionShowClientID, | 265 kOptionShowClientID, |
251 kOptionShowUploadsEnabled, | 266 kOptionShowUploadsEnabled, |
252 kOptionShowLastUploadAttemptTime, | 267 kOptionShowLastUploadAttemptTime, |
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
342 return EXIT_FAILURE; | 357 return EXIT_FAILURE; |
343 } | 358 } |
344 options.has_set_uploads_enabled = true; | 359 options.has_set_uploads_enabled = true; |
345 break; | 360 break; |
346 } | 361 } |
347 case kOptionSetLastUploadAttemptTime: { | 362 case kOptionSetLastUploadAttemptTime: { |
348 options.set_last_upload_attempt_time_string = optarg; | 363 options.set_last_upload_attempt_time_string = optarg; |
349 break; | 364 break; |
350 } | 365 } |
351 case kOptionNewReport: { | 366 case kOptionNewReport: { |
352 options.new_report_paths.push_back(base::FilePath(optarg)); | 367 options.new_report_paths.push_back( |
| 368 base::FilePath(UTF8ToFilePathStringType(optarg))); |
353 break; | 369 break; |
354 } | 370 } |
355 case kOptionUTC: { | 371 case kOptionUTC: { |
356 options.utc = true; | 372 options.utc = true; |
357 break; | 373 break; |
358 } | 374 } |
359 case kOptionHelp: { | 375 case kOptionHelp: { |
360 Usage(me); | 376 Usage(me); |
361 return EXIT_SUCCESS; | 377 return EXIT_SUCCESS; |
362 } | 378 } |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
401 options.new_report_paths.size(); | 417 options.new_report_paths.size(); |
402 const size_t set_operations = | 418 const size_t set_operations = |
403 options.has_set_uploads_enabled + | 419 options.has_set_uploads_enabled + |
404 (options.set_last_upload_attempt_time_string != nullptr); | 420 (options.set_last_upload_attempt_time_string != nullptr); |
405 | 421 |
406 if (show_operations + set_operations == 0) { | 422 if (show_operations + set_operations == 0) { |
407 ToolSupport::UsageHint(me, "nothing to do"); | 423 ToolSupport::UsageHint(me, "nothing to do"); |
408 return EXIT_FAILURE; | 424 return EXIT_FAILURE; |
409 } | 425 } |
410 | 426 |
411 scoped_ptr<CrashReportDatabase> database( | 427 scoped_ptr<CrashReportDatabase> database(CrashReportDatabase::Initialize( |
412 CrashReportDatabase::Initialize(base::FilePath(options.database))); | 428 base::FilePath(UTF8ToFilePathStringType(options.database)))); |
413 if (!database) { | 429 if (!database) { |
414 return EXIT_FAILURE; | 430 return EXIT_FAILURE; |
415 } | 431 } |
416 | 432 |
417 Settings* settings = database->GetSettings(); | 433 Settings* settings = database->GetSettings(); |
418 | 434 |
419 // Handle the “show” options before the “set” options so that when they’re | 435 // Handle the “show” options before the “set” options so that when they’re |
420 // specified together, the “show” option reflects the initial state. | 436 // specified together, the “show” option reflects the initial state. |
421 | 437 |
422 if (options.show_client_id) { | 438 if (options.show_client_id) { |
(...skipping 23 matching lines...) Expand all Loading... |
446 if (!settings->GetLastUploadAttemptTime(&last_upload_attempt_time)) { | 462 if (!settings->GetLastUploadAttemptTime(&last_upload_attempt_time)) { |
447 return EXIT_FAILURE; | 463 return EXIT_FAILURE; |
448 } | 464 } |
449 | 465 |
450 const char* prefix = | 466 const char* prefix = |
451 (show_operations > 1) ? "Last upload attempt time: " : ""; | 467 (show_operations > 1) ? "Last upload attempt time: " : ""; |
452 | 468 |
453 printf("%s%s (%ld)\n", | 469 printf("%s%s (%ld)\n", |
454 prefix, | 470 prefix, |
455 TimeToString(last_upload_attempt_time, options.utc).c_str(), | 471 TimeToString(last_upload_attempt_time, options.utc).c_str(), |
456 implicit_cast<long>(last_upload_attempt_time)); | 472 static_cast<long>(last_upload_attempt_time)); |
457 } | 473 } |
458 | 474 |
459 if (options.show_pending_reports) { | 475 if (options.show_pending_reports) { |
460 std::vector<CrashReportDatabase::Report> pending_reports; | 476 std::vector<CrashReportDatabase::Report> pending_reports; |
461 if (database->GetPendingReports(&pending_reports) != | 477 if (database->GetPendingReports(&pending_reports) != |
462 CrashReportDatabase::kNoError) { | 478 CrashReportDatabase::kNoError) { |
463 return EXIT_FAILURE; | 479 return EXIT_FAILURE; |
464 } | 480 } |
465 | 481 |
466 if (show_operations > 1) { | 482 if (show_operations > 1) { |
(...skipping 23 matching lines...) Expand all Loading... |
490 database->LookUpCrashReport(uuid, &report); | 506 database->LookUpCrashReport(uuid, &report); |
491 if (status == CrashReportDatabase::kNoError) { | 507 if (status == CrashReportDatabase::kNoError) { |
492 if (show_operations > 1) { | 508 if (show_operations > 1) { |
493 printf("Report %s:\n", uuid.ToString().c_str()); | 509 printf("Report %s:\n", uuid.ToString().c_str()); |
494 } | 510 } |
495 ShowReport(report, show_operations > 1 ? 2 : 0, options.utc); | 511 ShowReport(report, show_operations > 1 ? 2 : 0, options.utc); |
496 } else if (status == CrashReportDatabase::kReportNotFound) { | 512 } else if (status == CrashReportDatabase::kReportNotFound) { |
497 // If only asked to do one thing, a failure to find the single requested | 513 // If only asked to do one thing, a failure to find the single requested |
498 // report should result in a failure exit status. | 514 // report should result in a failure exit status. |
499 if (show_operations + set_operations == 1) { | 515 if (show_operations + set_operations == 1) { |
500 fprintf(stderr, "%s: Report not found\n", me.c_str()); | 516 fprintf( |
| 517 stderr, "%" PRFilePath ": Report not found\n", me.value().c_str()); |
501 return EXIT_FAILURE; | 518 return EXIT_FAILURE; |
502 } | 519 } |
503 printf("Report %s not found\n", uuid.ToString().c_str()); | 520 printf("Report %s not found\n", uuid.ToString().c_str()); |
504 } else { | 521 } else { |
505 return EXIT_FAILURE; | 522 return EXIT_FAILURE; |
506 } | 523 } |
507 } | 524 } |
508 | 525 |
509 if (options.has_set_uploads_enabled && | 526 if (options.has_set_uploads_enabled && |
510 !settings->SetUploadsEnabled(options.set_uploads_enabled)) { | 527 !settings->SetUploadsEnabled(options.set_uploads_enabled)) { |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
555 const char* prefix = (show_operations > 1) ? "New report ID: " : ""; | 572 const char* prefix = (show_operations > 1) ? "New report ID: " : ""; |
556 printf("%s%s\n", prefix, uuid.ToString().c_str()); | 573 printf("%s%s\n", prefix, uuid.ToString().c_str()); |
557 } | 574 } |
558 | 575 |
559 return EXIT_SUCCESS; | 576 return EXIT_SUCCESS; |
560 } | 577 } |
561 | 578 |
562 } // namespace | 579 } // namespace |
563 } // namespace crashpad | 580 } // namespace crashpad |
564 | 581 |
| 582 #if defined(OS_POSIX) |
565 int main(int argc, char* argv[]) { | 583 int main(int argc, char* argv[]) { |
566 return crashpad::DatabaseUtilMain(argc, argv); | 584 return crashpad::DatabaseUtilMain(argc, argv); |
567 } | 585 } |
| 586 #elif defined(OS_WIN) |
| 587 int wmain(int argc, wchar_t* argv[]) { |
| 588 scoped_ptr<char*[]> argv_as_utf8(new char*[argc]); |
| 589 std::vector<std::string> storage; |
| 590 storage.reserve(argc); |
| 591 for (int i = 0; i < argc; ++i) { |
| 592 storage.push_back(base::UTF16ToUTF8(argv[i])); |
| 593 argv_as_utf8[i] = &storage[i][0]; |
| 594 } |
| 595 return crashpad::DatabaseUtilMain(argc, argv_as_utf8.get()); |
| 596 } |
| 597 #endif // OS_POSIX |
OLD | NEW |