rewrite_minidumps_as_mimes.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. // Copyright 2018 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "components/minidump_uploader/rewrite_minidumps_as_mimes.h"
  5. #include <utility>
  6. #include "base/android/build_info.h"
  7. #include "base/android/jni_array.h"
  8. #include "base/android/jni_string.h"
  9. #include "base/metrics/histogram_macros.h"
  10. #include "base/notreached.h"
  11. #include "base/strings/stringprintf.h"
  12. #include "build/build_config.h"
  13. #include "components/crash/android/anr_skipped_reason.h"
  14. #include "components/minidump_uploader/minidump_uploader_jni_headers/CrashReportMimeWriter_jni.h"
  15. #include "components/version_info/android/channel_getter.h"
  16. #include "components/version_info/version_info.h"
  17. #include "third_party/crashpad/crashpad/handler/minidump_to_upload_parameters.h"
  18. #include "third_party/crashpad/crashpad/snapshot/exception_snapshot.h"
  19. #include "third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump.h"
  20. #include "third_party/crashpad/crashpad/util/file/file_writer.h"
  21. #include "third_party/crashpad/crashpad/util/net/http_body.h"
  22. #include "third_party/crashpad/crashpad/util/net/http_multipart_builder.h"
  23. #include "third_party/crashpad/crashpad/util/posix/signals.h"
  24. namespace minidump_uploader {
  25. namespace {
  26. #if BUILDFLAG(IS_ANDROID)
  27. // These values are persisted to logs. Entries should not be renumbered and
  28. // numeric values should never be reused.
  29. enum class ProcessedMinidumpCounts {
  30. kOther = 0,
  31. kBrowser = 1,
  32. kRenderer = 2,
  33. kGpu = 3,
  34. kUtility = 4,
  35. kMaxValue = kUtility
  36. };
  37. #endif // BUILDFLAG(IS_ANDROID)
  38. bool MimeifyReportWithKeyValuePairs(
  39. const crashpad::CrashReportDatabase::UploadReport& report,
  40. crashpad::HTTPMultipartBuilder* http_multipart_builder,
  41. std::vector<std::string>* crashes_key_value_arr,
  42. pid_t* pid) {
  43. crashpad::FileReader* reader = report.Reader();
  44. crashpad::FileOffset start_offset = reader->SeekGet();
  45. if (start_offset < 0) {
  46. return false;
  47. }
  48. // Ignore any errors that might occur when attempting to interpret the
  49. // minidump file. This may result in its being uploaded with few or no
  50. // parameters, but as long as there’s a dump file, the server can decide what
  51. // to do with it.
  52. std::map<std::string, std::string> parameters;
  53. crashpad::ProcessSnapshotMinidump minidump_process_snapshot;
  54. if (minidump_process_snapshot.Initialize(reader)) {
  55. parameters =
  56. BreakpadHTTPFormParametersFromMinidump(&minidump_process_snapshot);
  57. }
  58. if (!reader->SeekSet(start_offset)) {
  59. return false;
  60. }
  61. static constexpr char kMinidumpKey[] = "upload_file_minidump";
  62. static constexpr char kPtypeKey[] = "ptype";
  63. for (const auto& kv : parameters) {
  64. if (kv.first == kMinidumpKey) {
  65. LOG(WARNING) << "reserved key " << kv.first << ", discarding value "
  66. << kv.second;
  67. } else {
  68. http_multipart_builder->SetFormData(kv.first, kv.second);
  69. if (crashes_key_value_arr) {
  70. crashes_key_value_arr->push_back(kv.first);
  71. crashes_key_value_arr->push_back(kv.second);
  72. }
  73. #if BUILDFLAG(IS_ANDROID)
  74. if (kv.first == kPtypeKey) {
  75. const crashpad::ExceptionSnapshot* exception =
  76. minidump_process_snapshot.Exception();
  77. if (exception != nullptr) {
  78. const uint32_t signo = exception->Exception();
  79. ProcessedMinidumpCounts count_type;
  80. if (kv.second == "browser") {
  81. count_type = ProcessedMinidumpCounts::kBrowser;
  82. } else if (kv.second == "renderer") {
  83. count_type = ProcessedMinidumpCounts::kRenderer;
  84. } else if (kv.second == "gpu-process") {
  85. count_type = ProcessedMinidumpCounts::kGpu;
  86. } else if (kv.second == "utility") {
  87. count_type = ProcessedMinidumpCounts::kUtility;
  88. } else {
  89. count_type = ProcessedMinidumpCounts::kOther;
  90. }
  91. if (signo !=
  92. static_cast<uint32_t>(crashpad::Signals::kSimulatedSigno)) {
  93. UMA_HISTOGRAM_ENUMERATION(
  94. "Stability.Android.ProcessedRealMinidumps", count_type);
  95. }
  96. }
  97. }
  98. #endif // BUILDFLAG(IS_ANDROID)
  99. }
  100. }
  101. if (crashes_key_value_arr) {
  102. crashes_key_value_arr->push_back(kMinidumpKey);
  103. crashes_key_value_arr->push_back(report.uuid.ToString().c_str());
  104. }
  105. http_multipart_builder->SetFileAttachment(kMinidumpKey,
  106. report.uuid.ToString() + ".dmp",
  107. reader, "application/octet-stream");
  108. *pid = minidump_process_snapshot.ProcessID();
  109. return true;
  110. }
  111. bool MimeifyReportAndWriteToDirectory(
  112. const crashpad::CrashReportDatabase::UploadReport& report,
  113. const base::FilePath& dest_dir,
  114. std::vector<std::string>* crashes_key_value_arr) {
  115. crashpad::HTTPMultipartBuilder builder;
  116. pid_t pid;
  117. if (!MimeifyReportWithKeyValuePairs(report, &builder, crashes_key_value_arr,
  118. &pid)) {
  119. return false;
  120. }
  121. crashpad::FileWriter writer;
  122. if (!writer.Open(dest_dir.Append(base::StringPrintf(
  123. "%s.dmp%d", report.uuid.ToString().c_str(), pid)),
  124. crashpad::FileWriteMode::kCreateOrFail,
  125. crashpad::FilePermissions::kOwnerOnly)) {
  126. return false;
  127. }
  128. return WriteBodyToFile(builder.GetBodyStream().get(), &writer);
  129. }
  130. } // namespace
  131. bool MimeifyReport(const crashpad::CrashReportDatabase::UploadReport& report,
  132. crashpad::HTTPMultipartBuilder* http_multipart_builder,
  133. pid_t* pid) {
  134. return MimeifyReportWithKeyValuePairs(report, http_multipart_builder, nullptr,
  135. pid);
  136. }
  137. bool WriteBodyToFile(crashpad::HTTPBodyStream* body,
  138. crashpad::FileWriterInterface* writer) {
  139. uint8_t buffer[4096];
  140. crashpad::FileOperationResult bytes_read;
  141. while ((bytes_read = body->GetBytesBuffer(buffer, sizeof(buffer))) > 0) {
  142. writer->Write(buffer, bytes_read);
  143. }
  144. return bytes_read == 0;
  145. }
  146. void RewriteMinidumpsAsMIMEs(const base::FilePath& src_dir,
  147. const base::FilePath& dest_dir,
  148. std::vector<std::string>* crashes_key_value_arr) {
  149. std::unique_ptr<crashpad::CrashReportDatabase> db =
  150. crashpad::CrashReportDatabase::InitializeWithoutCreating(src_dir);
  151. if (!db) {
  152. return;
  153. }
  154. std::vector<crashpad::CrashReportDatabase::Report> reports;
  155. if (db->GetPendingReports(&reports) !=
  156. crashpad::CrashReportDatabase::kNoError) {
  157. return;
  158. }
  159. for (const auto& report : reports) {
  160. std::unique_ptr<const crashpad::CrashReportDatabase::UploadReport>
  161. upload_report;
  162. switch (db->GetReportForUploading(report.uuid,
  163. &upload_report,
  164. /* report_metrics= */ false)) {
  165. case crashpad::CrashReportDatabase::kBusyError:
  166. case crashpad::CrashReportDatabase::kReportNotFound:
  167. continue;
  168. case crashpad::CrashReportDatabase::kNoError:
  169. if (MimeifyReportAndWriteToDirectory(*upload_report.get(), dest_dir,
  170. crashes_key_value_arr)) {
  171. db->RecordUploadComplete(std::move(upload_report), std::string());
  172. } else {
  173. crashpad::Metrics::CrashUploadSkipped(
  174. crashpad::Metrics::CrashSkippedReason::kPrepareForUploadFailed);
  175. upload_report.reset();
  176. }
  177. db->DeleteReport(report.uuid);
  178. continue;
  179. case crashpad::CrashReportDatabase::kFileSystemError:
  180. case crashpad::CrashReportDatabase::kDatabaseError:
  181. crashpad::Metrics::CrashUploadSkipped(
  182. crashpad::Metrics::CrashSkippedReason::kDatabaseError);
  183. db->DeleteReport(report.uuid);
  184. continue;
  185. case crashpad::CrashReportDatabase::kCannotRequestUpload:
  186. NOTREACHED();
  187. db->DeleteReport(report.uuid);
  188. continue;
  189. }
  190. }
  191. }
  192. static void reportAnrUploadFailure(AnrSkippedReason reason) {
  193. UMA_HISTOGRAM_ENUMERATION("Crashpad.AnrUpload.Skipped", reason);
  194. }
  195. static void WriteAnrAsMime(crashpad::FileReader* anr_reader,
  196. crashpad::FileWriter* writer,
  197. const std::string& version_number,
  198. const std::string& anr_file_name) {
  199. static constexpr char kAnrKey[] = "anr_data";
  200. crashpad::HTTPMultipartBuilder builder;
  201. builder.SetFormData("version", version_number);
  202. builder.SetFormData("product", "Chrome_Android");
  203. std::string channel =
  204. version_info::GetChannelString(version_info::android::GetChannel());
  205. builder.SetFormData("channel", channel);
  206. // We can't use crashpad::AnnotationList::Get() as it contains a number of
  207. // fields which change on each Chrome restart.
  208. base::android::BuildInfo* info = base::android::BuildInfo::GetInstance();
  209. builder.SetFormData("android_build_id", info->android_build_id());
  210. builder.SetFormData("android_build_fp", info->android_build_fp());
  211. builder.SetFormData("sdk", base::StringPrintf("%d", info->sdk_int()));
  212. builder.SetFormData("device", info->device());
  213. builder.SetFormData("model", info->model());
  214. builder.SetFormData("brand", info->brand());
  215. builder.SetFormData("board", info->board());
  216. builder.SetFormData("installer_package_name", info->installer_package_name());
  217. builder.SetFormData("abi_name", info->abi_name());
  218. builder.SetFormData("custom_themes", info->custom_themes());
  219. builder.SetFormData("resources_version", info->resources_version());
  220. builder.SetFormData("gms_core_version", info->gms_version_code());
  221. // The firebase package name and version are used for deobfuscation, but will
  222. // only be accurate for the same version of chrome.
  223. if (version_number == version_info::GetVersionNumber() &&
  224. info->firebase_app_id()[0] != '\0') {
  225. builder.SetFormData("package", std::string(info->firebase_app_id()) + " v" +
  226. info->package_version_code() + " (" +
  227. info->package_version_name() + ")");
  228. }
  229. builder.SetFileAttachment(kAnrKey, anr_file_name, anr_reader,
  230. "application/octet-stream");
  231. if (!WriteBodyToFile(builder.GetBodyStream().get(), writer)) {
  232. reportAnrUploadFailure(AnrSkippedReason::kFilesystemWriteFailure);
  233. }
  234. }
  235. static void JNI_CrashReportMimeWriter_RewriteAnrsAsMIMEs(
  236. JNIEnv* env,
  237. const base::android::JavaParamRef<jobjectArray>& j_anr_files,
  238. const base::android::JavaParamRef<jobjectArray>& j_version_numbers,
  239. const base::android::JavaParamRef<jstring>& j_dest_dir) {
  240. std::vector<std::string> anr_files;
  241. AppendJavaStringArrayToStringVector(env, j_anr_files, &anr_files);
  242. std::vector<std::string> version_numbers;
  243. AppendJavaStringArrayToStringVector(env, j_version_numbers, &version_numbers);
  244. // We are assuming a 1:1 mapping between an ANR and its version number.
  245. DCHECK_EQ(anr_files.size(), version_numbers.size());
  246. std::string dest_dir;
  247. base::android::ConvertJavaStringToUTF8(env, j_dest_dir, &dest_dir);
  248. for (size_t i = 0; i < anr_files.size(); ++i) {
  249. crashpad::FileWriter writer;
  250. crashpad::FileReader reader;
  251. crashpad::UUID uuid;
  252. uuid.InitializeWithNew();
  253. std::string anr_file_name = uuid.ToString() + "_ANR.dmp";
  254. if (!reader.Open(base::FilePath(anr_files[i]))) {
  255. reportAnrUploadFailure(AnrSkippedReason::kFilesystemReadFailure);
  256. continue;
  257. }
  258. if (!writer.Open(base::FilePath(dest_dir).Append(anr_file_name),
  259. crashpad::FileWriteMode::kCreateOrFail,
  260. crashpad::FilePermissions::kOwnerOnly)) {
  261. reportAnrUploadFailure(AnrSkippedReason::kFilesystemWriteFailure);
  262. continue;
  263. }
  264. WriteAnrAsMime(&reader, &writer, version_numbers[i], anr_file_name);
  265. }
  266. }
  267. static void JNI_CrashReportMimeWriter_RewriteMinidumpsAsMIMEs(
  268. JNIEnv* env,
  269. const base::android::JavaParamRef<jstring>& j_src_dir,
  270. const base::android::JavaParamRef<jstring>& j_dest_dir) {
  271. std::string src_dir, dest_dir;
  272. base::android::ConvertJavaStringToUTF8(env, j_src_dir, &src_dir);
  273. base::android::ConvertJavaStringToUTF8(env, j_dest_dir, &dest_dir);
  274. RewriteMinidumpsAsMIMEs(base::FilePath(src_dir), base::FilePath(dest_dir),
  275. nullptr);
  276. }
  277. static base::android::ScopedJavaLocalRef<jobjectArray>
  278. JNI_CrashReportMimeWriter_RewriteMinidumpsAsMIMEsAndGetCrashKeys(
  279. JNIEnv* env,
  280. const base::android::JavaParamRef<jstring>& j_src_dir,
  281. const base::android::JavaParamRef<jstring>& j_dest_dir) {
  282. std::string src_dir, dest_dir;
  283. base::android::ConvertJavaStringToUTF8(env, j_src_dir, &src_dir);
  284. base::android::ConvertJavaStringToUTF8(env, j_dest_dir, &dest_dir);
  285. std::vector<std::string> key_value_arr;
  286. RewriteMinidumpsAsMIMEs(base::FilePath(src_dir), base::FilePath(dest_dir),
  287. &key_value_arr);
  288. return base::android::ToJavaArrayOfStrings(env, key_value_arr);
  289. }
  290. } // namespace minidump_uploader