1 : // Copyright 2016 Google Inc. All Rights Reserved.
2 : //
3 : // Licensed under the Apache License, Version 2.0 (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
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 :
15 : #include "syzygy/kasko/kasko_upload_app.h"
16 :
17 : #include "base/bind.h"
18 : #include "base/files/file_util.h"
19 : #include "syzygy/kasko/crash_keys_serialization.h"
20 : #include "syzygy/kasko/reporter.h"
21 :
22 m : namespace kasko {
23 :
24 m : namespace {
25 :
26 : // URL of the default crash handler.
27 : #define KASKO_DEFAULT_UPLOAD_URL "https://clients2.google.com/cr/report"
28 :
29 m : const char kUsageFormatStr[] =
30 m : "Usage: %ls --minidump=<MINIDUMP> [options]\n"
31 m : "\n"
32 m : " A tool that uploads minidumps and crashkeys to a crash server.\n"
33 m : "\n"
34 m : "Required parameters\n"
35 m : " --minidump=<MINIDUMP>"
36 m : " Path to the minidump file to upload.\n"
37 m : "\n"
38 m : "Optional parameters\n"
39 m : " --crash-keys=<CRASHKEYS>\n"
40 m : " Path to the JSON formatted crash keys to upload. Defaults to the\n"
41 m : " filename obtained by replacing the minidump extension with .kys.\n"
42 m : " --upload-url=<URL>\n"
43 m : " URL where the crash should be upload. Defaults to:\n"
44 m : " " KASKO_DEFAULT_UPLOAD_URL "\n"
45 m : "\n";
46 :
47 : // Callback that is invoked upon successful upload.
48 m : void OnUploadCallback(
49 m : base::string16* output_report_id,
50 m : const base::string16& report_id,
51 m : const base::FilePath& minidump_path,
52 m : const std::map<base::string16, base::string16>& crash_keys) {
53 m : DCHECK_NE(static_cast<base::string16*>(nullptr), output_report_id);
54 m : *output_report_id = report_id;
55 m : }
56 :
57 m : } // namespace
58 :
59 : #define KASKO_MINIDUMP_SWITCH "minidump"
60 :
61 : // A small helper macro for converting an 8-bit char string to a 16-bit char
62 : // string.
63 : #define WIDEN_IMPL(x) L ## x
64 : #define WIDEN(x) WIDEN_IMPL(x)
65 :
66 m : const char KaskoUploadApp::kMinidumpSwitch[] = KASKO_MINIDUMP_SWITCH;
67 m : const char KaskoUploadApp::kCrashKeysSwitch[] = "crash-keys";
68 m : const char KaskoUploadApp::kUploadUrlSwitch[] = "upload-url";
69 m : const base::char16 KaskoUploadApp::kDefaultUploadUrl[] =
70 m : WIDEN(KASKO_DEFAULT_UPLOAD_URL);
71 :
72 m : KaskoUploadApp::KaskoUploadApp()
73 m : : application::AppImplBase("Kasko Upload") {
74 m : }
75 :
76 m : bool KaskoUploadApp::ParseCommandLine(const base::CommandLine* command_line) {
77 m : DCHECK_NE(static_cast<base::CommandLine*>(nullptr), command_line);
78 :
79 m : if (!command_line->HasSwitch(kMinidumpSwitch)) {
80 m : PrintUsage(command_line->GetProgram(),
81 m : "You must specify --" KASKO_MINIDUMP_SWITCH ".");
82 m : return false;
83 m : }
84 :
85 m : minidump_path_ = command_line->GetSwitchValuePath(kMinidumpSwitch);
86 m : LOG(INFO) << "Using minidump path: " << minidump_path_.value();
87 :
88 m : if (command_line->HasSwitch(kCrashKeysSwitch)) {
89 m : crash_keys_path_ = command_line->GetSwitchValuePath(kCrashKeysSwitch);
90 m : LOG(INFO) << "Using crash-keys path: " << crash_keys_path_.value();
91 m : } else {
92 m : crash_keys_path_ = minidump_path_.ReplaceExtension(L".kys");
93 m : LOG(INFO) << "Using default crash-keys path: " << crash_keys_path_.value();
94 m : }
95 :
96 m : if (command_line->HasSwitch(kUploadUrlSwitch)) {
97 m : upload_url_ = command_line->GetSwitchValueNative(kUploadUrlSwitch);
98 m : LOG(INFO) << "Using upload URL: " << upload_url_;
99 m : } else {
100 m : upload_url_ = kDefaultUploadUrl;
101 m : LOG(INFO) << "Using default upload URL: " << upload_url_;
102 m : }
103 :
104 m : return true;
105 m : }
106 :
107 m : int KaskoUploadApp::Run() {
108 m : if (!base::PathExists(crash_keys_path_)) {
109 m : LOG(ERROR) << "Crash keys file not found: " << crash_keys_path_.value();
110 m : return kReturnCodeCrashKeysFileMissing;
111 m : }
112 :
113 m : std::map<base::string16, base::string16> crash_keys;
114 m : if (!ReadCrashKeysFromFile(crash_keys_path_, &crash_keys)) {
115 m : LOG(ERROR) << "Failed to read crash keys from file: "
116 m : << crash_keys_path_.value();
117 m : return kReturnCodeCrashKeysFileMalformed;
118 m : }
119 :
120 m : for (const auto& kv : crash_keys) {
121 m : LOG(INFO) << "Read crash key \"" << kv.first << "\": \"" << kv.second
122 m : << "\"";
123 m : }
124 :
125 : // Ensure that the minimum set of necessary crash keys is present.
126 m : static const base::char16* kRequiredCrashKeys[] = {
127 m : L"prod", L"ver", L"platform", L"ptype", L"guid", L"channel" };
128 m : size_t missing_keys = 0;
129 m : for (size_t i = 0; i < arraysize(kRequiredCrashKeys); ++i) {
130 m : if (crash_keys.count(kRequiredCrashKeys[i]) == 0) {
131 m : ++missing_keys;
132 m : LOG(ERROR) << "Missing required crash key \"" << kRequiredCrashKeys[i]
133 m : << "\".";
134 m : }
135 m : }
136 m : if (missing_keys > 0)
137 m : return kReturnCodeCrashKeysAbsent;
138 :
139 m : if (!base::PathExists(minidump_path_)) {
140 m : LOG(ERROR) << "Minidump file not found: " << minidump_path_.value();
141 m : return kReturnCodeMinidumpFileMissing;
142 m : }
143 :
144 m : base::string16 report_id;
145 m : Reporter::OnUploadCallback on_upload = base::Bind(
146 m : &OnUploadCallback, base::Unretained(&report_id));
147 m : if (!Reporter::UploadCrashReport(on_upload, upload_url_, minidump_path_,
148 m : crash_keys)) {
149 m : LOG(ERROR) << "Failed to upload crash report.";
150 m : return kReturnCodeUploadFailed;
151 m : }
152 :
153 m : LOG(INFO) << "Report successfully uploaded with report ID: " << report_id;
154 m : return kReturnCodeSuccess;
155 m : }
156 :
157 m : void KaskoUploadApp::PrintUsage(const base::FilePath& program,
158 m : const base::StringPiece& message) {
159 :
160 m : if (!message.empty()) {
161 m : ::fwrite(message.data(), 1, message.length(), out());
162 m : ::fprintf(out(), "\n\n");
163 m : }
164 :
165 m : ::fprintf(out(), kUsageFormatStr, program.BaseName().value().c_str());
166 m : }
167 :
168 m : } // namespace kasko
|