From: uazo Date: Tue, 15 Jun 2021 11:49:43 +0000 Subject: Logcat crash reports UI Original License: GPL-2.0-or-later - https://spdx.org/licenses/GPL-2.0-or-later.html License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html --- .../crash/MinidumpUploadServiceImpl.java | 22 +++ .../crash_upload_list_android.cc | 25 ++- .../crash_upload_list_android.h | 1 + chrome/browser/net/chrome_network_delegate.cc | 7 + chrome/browser/ui/BUILD.gn | 1 + chrome/browser/ui/webui/crashes_ui.cc | 176 ++++++++++++++++-- .../crash/core/browser/crashes_ui_util.cc | 4 + .../crash/core/browser/crashes_ui_util.h | 2 + .../crash/core/browser/resources/crashes.css | 67 ++++++- .../crash/core/browser/resources/crashes.html | 17 ++ .../crash/core/browser/resources/crashes.ts | 94 +++------- components/crash_strings.grdp | 22 ++- .../minidump_uploader/CrashFileManager.java | 7 +- .../MinidumpUploadCallable.java | 21 +-- .../minidump_uploader/MinidumpUploader.java | 29 +-- .../upload_list/text_log_upload_list.cc | 1 + components/upload_list/upload_list.cc | 14 ++ components/upload_list/upload_list.h | 9 + 18 files changed, 385 insertions(+), 134 deletions(-) diff --git a/chrome/android/java/src/org/chromium/chrome/browser/crash/MinidumpUploadServiceImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/crash/MinidumpUploadServiceImpl.java --- a/chrome/android/java/src/org/chromium/chrome/browser/crash/MinidumpUploadServiceImpl.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/crash/MinidumpUploadServiceImpl.java @@ -44,6 +44,11 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.concurrent.atomic.AtomicBoolean; +import org.chromium.base.task.AsyncTask; +import org.chromium.base.task.PostTask; +import org.chromium.base.task.TaskTraits; +import org.chromium.chrome.browser.crash.LogcatExtractionRunnable; + /** * Service that is responsible for uploading crash minidumps to the Google crash server. */ @@ -92,6 +97,23 @@ public class MinidumpUploadServiceImpl extends MinidumpUploadService.Impl { getService().setIntentRedelivery(true); } + @CalledByNative + public static void requestNewExtraction() { + CrashFileManager crashFileManager = + new CrashFileManager(ContextUtils.getApplicationContext().getCacheDir()); + + // Append logcat output to minidumps where are missing + // getMinidumpsSansLogcat() also extract new files from crashpad + File[] minidumpsSansLogcat = crashFileManager.getMinidumpsSansLogcat(); + if (minidumpsSansLogcat.length >= 1) { + for (int i = 0; i < minidumpsSansLogcat.length; ++i) { + File minidump = minidumpsSansLogcat[i]; + AsyncTask.THREAD_POOL_EXECUTOR.execute( + new LogcatExtractionRunnable(minidump)); + } + } + } + /** * Schedules uploading of all pending minidumps, using the JobScheduler API. */ diff --git a/chrome/browser/crash_upload_list/crash_upload_list_android.cc b/chrome/browser/crash_upload_list/crash_upload_list_android.cc --- a/chrome/browser/crash_upload_list/crash_upload_list_android.cc +++ b/chrome/browser/crash_upload_list/crash_upload_list_android.cc @@ -14,6 +14,7 @@ #include "base/metrics/histogram_macros_local.h" #include "chrome/android/chrome_jni_headers/MinidumpUploadServiceImpl_jni.h" #include "ui/base/text/bytes_formatting.h" +#include "base/strings/string_util.h" namespace { @@ -76,16 +77,26 @@ void CrashUploadListAndroid::RequestSingleUpload(const std::string& local_id) { Java_MinidumpUploadServiceImpl_tryUploadCrashDumpWithLocalId(env, j_local_id); } +void CrashUploadListAndroid::RequestNewExtraction() { + JNIEnv* env = base::android::AttachCurrentThread(); + Java_MinidumpUploadServiceImpl_requestNewExtraction(env); +} + void CrashUploadListAndroid::LoadUnsuccessfulUploadList( std::vector>* uploads) { const char pending_uploads[] = ".dmp"; const char skipped_uploads[] = ".skipped"; const char manually_forced_uploads[] = ".forced"; + const char zipped_uploads[] = ".zip"; base::FileEnumerator files(upload_log_path().DirName(), false, base::FileEnumerator::FILES); for (base::FilePath file = files.Next(); !file.empty(); file = files.Next()) { UploadList::UploadInfo::State upload_state; + if (base::EndsWith(file.value(), zipped_uploads, base::CompareCase::INSENSITIVE_ASCII)) { + // skip zip files + continue; + } if (file.value().find(manually_forced_uploads) != std::string::npos) { RecordUnsuccessfulUploadListState(UnsuccessfulUploadListState::FORCED); upload_state = UploadList::UploadInfo::State::Pending_UserRequested; @@ -117,6 +128,8 @@ void CrashUploadListAndroid::LoadUnsuccessfulUploadList( continue; } + std::string file_path = file.value(); + // Crash reports can have multiple extensions (e.g. foo.dmp, foo.dmp.try1, // foo.skipped.try0). file = file.BaseName(); @@ -136,7 +149,15 @@ void CrashUploadListAndroid::LoadUnsuccessfulUploadList( RecordUnsuccessfulUploadListState( UnsuccessfulUploadListState::ADDING_AN_UPLOAD_ENTRY); id = id.substr(pos + 1); - uploads->push_back(std::make_unique( - id, info.creation_time, upload_state, ui::FormatBytes(file_size))); + // Since current thread is an IO thread + // to avoid failed DCHECK ThreadRestrictions::AssertSingletonAllowed() + // remove ui::FormatBytes(): dcheck fail because it use base::FormatDouble() + // and LazyInstance::DestructorAtExit(). + // also "upload.file_size" is unused. + std::u16string file_size_string; + auto upload = std::make_unique( + id, info.creation_time, upload_state, file_size_string); + upload->file_path = file_path; + uploads->push_back(std::move(upload)); } } diff --git a/chrome/browser/crash_upload_list/crash_upload_list_android.h b/chrome/browser/crash_upload_list/crash_upload_list_android.h --- a/chrome/browser/crash_upload_list/crash_upload_list_android.h +++ b/chrome/browser/crash_upload_list/crash_upload_list_android.h @@ -35,6 +35,7 @@ class CrashUploadListAndroid : public TextLogUploadList { std::vector> LoadUploadList() override; void RequestSingleUpload(const std::string& local_id) override; + void RequestNewExtraction() override; private: void LoadUnsuccessfulUploadList( diff --git a/chrome/browser/net/chrome_network_delegate.cc b/chrome/browser/net/chrome_network_delegate.cc --- a/chrome/browser/net/chrome_network_delegate.cc +++ b/chrome/browser/net/chrome_network_delegate.cc @@ -150,6 +150,13 @@ bool IsAccessAllowedAndroid(const base::FilePath& path) { if (external_storage_path.IsParent(path)) return true; + // access to the crash folder is allowed for the download by the user + base::FilePath cache_dir; + base::android::GetCacheDirectory(&cache_dir); + base::FilePath upload_log_path = cache_dir.Append("Crash Reports"); + if (upload_log_path.IsParent(path)) + return true; + std::vector allowlist; std::vector all_download_dirs = base::android::GetAllPrivateDownloadsDirectories(); diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn --- a/chrome/browser/ui/BUILD.gn +++ b/chrome/browser/ui/BUILD.gn @@ -665,6 +665,7 @@ static_library("ui") { "//third_party/re2", "//third_party/webrtc_overrides:webrtc_component", "//third_party/zlib", + "//third_party/zlib/google:zip", "//ui/accessibility", "//ui/base", "//ui/base:data_exchange", diff --git a/chrome/browser/ui/webui/crashes_ui.cc b/chrome/browser/ui/webui/crashes_ui.cc --- a/chrome/browser/ui/webui/crashes_ui.cc +++ b/chrome/browser/ui/webui/crashes_ui.cc @@ -37,6 +37,19 @@ #include "google_apis/gaia/gaia_auth_util.h" #include "ui/base/resource/resource_bundle.h" +#include "base/logging.h" +#include "base/debug/dump_without_crashing.h" +#include "base/files/file_util.h" +#include "base/files/file_enumerator.h" +#include "base/files/scoped_temp_dir.h" +#include "base/task/task_traits.h" +#include "base/task/thread_pool.h" +#if BUILDFLAG(IS_ANDROID) +#include "base/android/path_utils.h" +#endif +#include "net/base/filename_util.h" +#include "third_party/zlib/google/zip.h" + #if BUILDFLAG(IS_CHROMEOS_ASH) #include "chromeos/ash/components/dbus/debug_daemon/debug_daemon_client.h" #endif @@ -72,6 +85,10 @@ void CreateAndAddCrashesUIHTMLSource(Profile* profile) { source->SetDefaultResource(IDR_CRASH_CRASHES_HTML); } +constexpr base::TaskTraits kLoadingTaskTraits = { + base::MayBlock(), base::TaskPriority::USER_BLOCKING, + base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}; + //////////////////////////////////////////////////////////////////////////////// // // CrashesDOMHandler @@ -81,7 +98,7 @@ void CreateAndAddCrashesUIHTMLSource(Profile* profile) { // The handler for Javascript messages for the chrome://crashes/ page. class CrashesDOMHandler : public WebUIMessageHandler { public: - CrashesDOMHandler(); + CrashesDOMHandler(content::WebContents* web_contents); CrashesDOMHandler(const CrashesDOMHandler&) = delete; CrashesDOMHandler& operator=(const CrashesDOMHandler&) = delete; @@ -97,6 +114,8 @@ class CrashesDOMHandler : public WebUIMessageHandler { // Asynchronously fetches the list of crashes. Called from JS. void HandleRequestCrashes(const base::Value::List& args); + void RequestCrashesList(); + #if BUILDFLAG(IS_CHROMEOS_ASH) // Asynchronously triggers crash uploading. Called from JS. void HandleRequestUploads(const base::Value::List& args); @@ -108,14 +127,30 @@ class CrashesDOMHandler : public WebUIMessageHandler { // Asynchronously requests a user triggered upload. Called from JS. void HandleRequestSingleCrashUpload(const base::Value::List& args); + std::string RequestSingleUpload(const std::string& local_id) const; + void RequestSingleUploadCallback(const std::string& local_id, const std::string& filename); + + // Asynchronously requests a user log extraction. Called from JS. + void HandleRequestNewExtraction(const base::Value::List& args); + void RequestNewExtraction(); + + // Requests remove all crash files. Called from JS. + void HandleRequestClearAll(const base::Value::List& args); + void ClearAll(); + scoped_refptr upload_list_; bool list_available_; bool first_load_; + raw_ptr web_contents_; }; -CrashesDOMHandler::CrashesDOMHandler() - : list_available_(false), first_load_(true) { +CrashesDOMHandler::CrashesDOMHandler(content::WebContents* web_contents) + : list_available_(false), first_load_(true), + web_contents_(web_contents) { upload_list_ = CreateCrashUploadList(); +#if !BUILDFLAG(IS_ANDROID) + web_contents_ = nullptr; +#endif } CrashesDOMHandler::~CrashesDOMHandler() { @@ -141,10 +176,24 @@ void CrashesDOMHandler::RegisterMessages() { crash_reporter::kCrashesUIRequestSingleCrashUpload, base::BindRepeating(&CrashesDOMHandler::HandleRequestSingleCrashUpload, base::Unretained(this))); + + web_ui()->RegisterMessageCallback( + crash_reporter::kCrashesUIHandleClearAll, + base::BindRepeating(&CrashesDOMHandler::HandleRequestClearAll, + base::Unretained(this))); + + web_ui()->RegisterMessageCallback( + crash_reporter::kCrashesUIHandleRequestNewExtraction, + base::BindRepeating(&CrashesDOMHandler::HandleRequestNewExtraction, + base::Unretained(this))); } void CrashesDOMHandler::HandleRequestCrashes(const base::Value::List& args) { AllowJavascript(); + RequestCrashesList(); +} + +void CrashesDOMHandler::RequestCrashesList() { if (first_load_) { first_load_ = false; if (list_available_) @@ -176,8 +225,7 @@ void CrashesDOMHandler::OnUploadListAvailable() { } void CrashesDOMHandler::UpdateUI() { - bool crash_reporting_enabled = - ChromeMetricsServiceAccessor::IsMetricsAndCrashReportingEnabled(); + bool crash_reporting_enabled = true; bool system_crash_reporter = false; #if BUILDFLAG(IS_CHROMEOS) @@ -225,14 +273,117 @@ void CrashesDOMHandler::UpdateUI() { void CrashesDOMHandler::HandleRequestSingleCrashUpload( const base::Value::List& args) { - // Only allow manual uploads if crash uploads aren’t disabled by policy. - if (!ChromeMetricsServiceAccessor::IsMetricsAndCrashReportingEnabled() && - IsMetricsReportingPolicyManaged()) { - return; + std::string local_id = args[0].GetString(); + base::ThreadPool::PostTaskAndReplyWithResult( + FROM_HERE, kLoadingTaskTraits, + base::BindOnce(&CrashesDOMHandler::RequestSingleUpload, base::Unretained(this), local_id), + base::BindOnce(&CrashesDOMHandler::RequestSingleUploadCallback, base::Unretained(this), local_id)); +} + +std::string CrashesDOMHandler::RequestSingleUpload(const std::string& local_id) const { +#if BUILDFLAG(IS_ANDROID) + // get crash file path + std::string info_file_path = upload_list_->GetFilePathByLocalId(local_id); + if (info_file_path.empty()) { + LOG(ERROR) << "Crash report: file path is not set for " << local_id; + return std::string(); + } + base::FilePath crash_file_path(info_file_path); + + // get android crash report dir + base::FilePath cache_dir; + base::android::GetCacheDirectory(&cache_dir); + base::FilePath upload_log_path = cache_dir.Append("Crash Reports"); + + // crash reports can have multiple extensions (e.g. foo.dmp, foo.dmp.try1, + // foo.skipped.try0), remove it + base::FilePath zip_file_name = crash_file_path; + while (zip_file_name != zip_file_name.RemoveExtension()) + zip_file_name = zip_file_name.RemoveExtension(); + + // make zip file name, like "ec708a7b-cb17-44e7-8dae-e32f6c45cb8c.zip" + zip_file_name = upload_log_path.Append(zip_file_name.BaseName()) + .AddExtensionASCII(".zip"); + // since the download is always allowed, the generation takes place only + // at the first request, so if exists return it + if (base::PathExists(zip_file_name)) + return zip_file_name.value(); + + // original code remove the file immediately after upload. + // we changed this behavior but it is still possible that the file no longer exists + // because in uploads.log it could be indicated but the file was deleted by self-cleaning + if (!base::PathExists(crash_file_path)) { + LOG(ERROR) << "Crash report: file " << crash_file_path + << " no more available"; + return std::string(); } - std::string local_id = args[0].GetString(); - upload_list_->RequestSingleUploadAsync(local_id); + std::vector files_list; + files_list.push_back(crash_file_path.BaseName()); + + // open zip file + base::File zip_f(zip_file_name, + base::File::FLAG_CREATE | base::File::FLAG_WRITE); + auto result = zip::ZipFiles(crash_file_path.DirName(), files_list, zip_f.GetPlatformFile()); + zip_f.Close(); + if (result) { + return zip_file_name.value(); + } +#endif + LOG(ERROR) << "Crash report: cannot create zip content"; + return std::string(); +} + +void CrashesDOMHandler::RequestSingleUploadCallback(const std::string& local_id, + const std::string& file_name) { +#if BUILDFLAG(IS_ANDROID) + if (!file_name.empty()) { + upload_list_->RequestSingleUploadAsync(local_id); + + base::FilePath file_path(file_name); + web_contents_->GetController().LoadURL( + net::FilePathToFileURL(file_path), {}, {}, {}); + } +#endif +} + +void CrashesDOMHandler::HandleRequestNewExtraction( + const base::Value::List& args) { + base::ThreadPool::PostTask( + FROM_HERE, kLoadingTaskTraits, + base::BindOnce(&CrashesDOMHandler::RequestNewExtraction, base::Unretained(this))); +} + +void CrashesDOMHandler::RequestNewExtraction() { + base::debug::DumpWithoutCrashing(); + // ask java to get file from crashpad and to add logcat + upload_list_->RequestNewExtraction(); +} + +void CrashesDOMHandler::HandleRequestClearAll( + const base::Value::List& args) { + base::ThreadPool::PostTaskAndReply( + FROM_HERE, kLoadingTaskTraits, + base::BindOnce(&CrashesDOMHandler::ClearAll, base::Unretained(this)), + base::BindOnce(&CrashesDOMHandler::RequestCrashesList, base::Unretained(this))); +} + +void CrashesDOMHandler::ClearAll() { +#if BUILDFLAG(IS_ANDROID) + // get android crash report dir + base::FilePath cache_dir; + base::android::GetCacheDirectory(&cache_dir); + base::FilePath upload_log_path = cache_dir.Append("Crash Reports"); + + base::FileEnumerator dir_enum( + upload_log_path, + /*recursive=*/false, base::FileEnumerator::FILES); + base::FilePath full_name; + while (full_name = dir_enum.Next(), !full_name.empty()) { + // remove all files, don't care for result + base::DeleteFile(full_name); + } +#endif } } // namespace @@ -244,7 +395,8 @@ void CrashesDOMHandler::HandleRequestSingleCrashUpload( /////////////////////////////////////////////////////////////////////////////// CrashesUI::CrashesUI(content::WebUI* web_ui) : WebUIController(web_ui) { - web_ui->AddMessageHandler(std::make_unique()); + web_ui->AddMessageHandler(std::make_unique( + web_ui->GetWebContents())); // Set up the chrome://crashes/ source. CreateAndAddCrashesUIHTMLSource(Profile::FromWebUI(web_ui)); diff --git a/components/crash/core/browser/crashes_ui_util.cc b/components/crash/core/browser/crashes_ui_util.cc --- a/components/crash/core/browser/crashes_ui_util.cc +++ b/components/crash/core/browser/crashes_ui_util.cc @@ -39,6 +39,8 @@ const CrashesUILocalizedString kCrashesUILocalizedStrings[] = { {"uploadId", IDS_CRASH_REPORT_UPLOADED_ID}, {"uploadNowLinkText", IDS_CRASH_UPLOAD_NOW_LINK_TEXT}, {"uploadTime", IDS_CRASH_REPORT_UPLOADED_TIME}, + {"clearAll", IDS_CRASH_CLEAR_ALL_TEXT}, + {"extractNow", IDS_CRASH_EXTRACT_NOW_TEXT}, }; const size_t kCrashesUILocalizedStringsCount = @@ -52,6 +54,8 @@ const char kCrashesUIRequestCrashUpload[] = "requestCrashUpload"; const char kCrashesUIShortProductName[] = "shortProductName"; const char kCrashesUIUpdateCrashList[] = "update-crash-list"; const char kCrashesUIRequestSingleCrashUpload[] = "requestSingleCrashUpload"; +const char kCrashesUIHandleClearAll[] = "requestClearAll"; +const char kCrashesUIHandleRequestNewExtraction[] = "requestNewExtraction"; std::string UploadInfoStateAsString(UploadList::UploadInfo::State state) { switch (state) { diff --git a/components/crash/core/browser/crashes_ui_util.h b/components/crash/core/browser/crashes_ui_util.h --- a/components/crash/core/browser/crashes_ui_util.h +++ b/components/crash/core/browser/crashes_ui_util.h @@ -34,6 +34,8 @@ extern const char kCrashesUIRequestCrashUpload[]; extern const char kCrashesUIShortProductName[]; extern const char kCrashesUIUpdateCrashList[]; extern const char kCrashesUIRequestSingleCrashUpload[]; +extern const char kCrashesUIHandleClearAll[]; +extern const char kCrashesUIHandleRequestNewExtraction[]; // Converts and appends the most recent uploads to |out_value|. void UploadListToValue(UploadList* upload_list, base::Value::List* out_value); diff --git a/components/crash/core/browser/resources/crashes.css b/components/crash/core/browser/resources/crashes.css --- a/components/crash/core/browser/resources/crashes.css +++ b/components/crash/core/browser/resources/crashes.css @@ -3,7 +3,9 @@ * found in the LICENSE file. */ body { - margin: 20px; + margin: 0; + padding: 1em; + font-size: 100%; } h1 { @@ -27,7 +29,6 @@ html[dir=rtl] h1 { background-color: rgb(235, 239, 250); border: 1px solid #bbb; border-radius: 2px; - display: flex; font-size: 100%; padding: 4px; } @@ -80,3 +81,65 @@ html[dir=rtl] h1 { .not-uploaded { color: #a0a0a0; } + +label { + float: right; +} + +#countBanner > div { + display: flex; + justify-content: flex-end; + margin-top: 10px; +} + +.spinner { + width: 50px; + height: 40px; + text-align: center; + font-size: 10px; +} + +.spinner > div { + background-color: #333; + height: 100%; + width: 6px; + display: inline-block; + + -webkit-animation: sk-stretchdelay 1.2s infinite ease-in-out; + animation: sk-stretchdelay 1.2s infinite ease-in-out; +} + +.spinner .rect2 { + -webkit-animation-delay: -1.1s; + animation-delay: -1.1s; +} + +.spinner .rect3 { + -webkit-animation-delay: -1.0s; + animation-delay: -1.0s; +} + +.spinner .rect4 { + -webkit-animation-delay: -0.9s; + animation-delay: -0.9s; +} + +.spinner .rect5 { + -webkit-animation-delay: -0.8s; + animation-delay: -0.8s; +} + +@-webkit-keyframes sk-stretchdelay { + 0%, 40%, 100% { -webkit-transform: scaleY(0.4) } + 20% { -webkit-transform: scaleY(1.0) } +} + +@keyframes sk-stretchdelay { + 0%, 40%, 100% { + transform: scaleY(0.4); + -webkit-transform: scaleY(0.4); + } 20% { + transform: scaleY(1.0); + -webkit-transform: scaleY(1.0); + } +} diff --git a/components/crash/core/browser/resources/crashes.html b/components/crash/core/browser/resources/crashes.html --- a/components/crash/core/browser/resources/crashes.html +++ b/components/crash/core/browser/resources/crashes.html @@ -1,6 +1,7 @@ + @@ -39,6 +40,22 @@ $i18n{showDeveloperDetails} +
+
+ + + +
diff --git a/components/crash/core/browser/resources/crashes.ts b/components/crash/core/browser/resources/crashes.ts --- a/components/crash/core/browser/resources/crashes.ts +++ b/components/crash/core/browser/resources/crashes.ts @@ -12,7 +12,7 @@ import './strings.m.js'; import {assert} from 'chrome://resources/js/assert_ts.js'; import {addWebUiListener} from 'chrome://resources/js/cr.js'; import {loadTimeData} from 'chrome://resources/js/load_time_data.js'; -import {appendParam, getRequiredElement} from 'chrome://resources/js/util_ts.js'; +import {getRequiredElement} from 'chrome://resources/js/util_ts.js'; /* Id for tracking automatic refresh of crash list. */ let refreshCrashListId: number|undefined = undefined; @@ -44,10 +44,7 @@ interface CrashData { interface UpdateCrashListParams { enabled: boolean; dynamicBackend: boolean; - manualUploads: boolean; crashes: CrashData[]; - version: string; - os: string; isGoogleAccount: boolean; } @@ -57,10 +54,7 @@ interface UpdateCrashListParams { function updateCrashList({ enabled, dynamicBackend, - manualUploads, crashes, - version, - os, isGoogleAccount, }: UpdateCrashListParams) { getRequiredElement('crashesCount').textContent = loadTimeData.getStringF( @@ -70,6 +64,7 @@ function updateCrashList({ getRequiredElement('disabledMode').hidden = enabled; getRequiredElement('crashUploadStatus').hidden = !enabled || !dynamicBackend; + getRequiredElement('spinner').hidden = true; const template = crashList.querySelector('template'); assert(template); @@ -150,22 +145,16 @@ function updateCrashList({ assert(uploadTimeCell); uploadTimeCell.textContent = crash.upload_time || ''; - sendNowButton.remove(); - fileBugButton.onclick = () => fileBug(crash.id, os, version); + fileBugButton.remove(); } else { uploadId.remove(); uploadTime.remove(); fileBugButton.remove(); - // Do not allow crash submission if the Chromium build does not support - // it, or if the user already requested it. - if (!manualUploads || crash.state === State.PENDING_USER_REQUESTED) { - sendNowButton.remove(); - } - sendNowButton.onclick = (_e: Event) => { - sendNowButton.disabled = true; - chrome.send('requestSingleCrashUpload', [crash.local_id]); - }; } + sendNowButton.onclick = (_e: Event) => { + sendNowButton.disabled = true; + chrome.send('requestSingleCrashUpload', [crash.local_id]); + }; const fileSize = clone.querySelector('.file-size'); assert(fileSize); @@ -183,52 +172,6 @@ function updateCrashList({ getRequiredElement('noCrashes').hidden = crashes.length !== 0; } -/** - * Opens a new tab/window to report the crash to crbug. - * @param The crash report ID. - * @param The OS name. - * @param The product version. - */ -function fileBug(crashId: string, os: string, version: string) { - const commentLines = [ - 'IMPORTANT: Your crash has already been automatically reported ' + - 'to our crash system. Please file this bug only if you can provide ' + - 'more information about it.', - '', - '', - 'Chrome Version: ' + version, - 'Operating System: ' + os, - '', - 'URL (if applicable) where crash occurred:', - '', - 'Can you reproduce this crash?', - '', - 'What steps will reproduce this crash? (If it\'s not ' + - 'reproducible, what were you doing just before the crash?)', - '1.', - '2.', - '3.', - '', - '****DO NOT CHANGE BELOW THIS LINE****', - 'Crash ID: crash/' + crashId, - ]; - const params: {[key: string]: string} = { - template: 'Crash Report', - comment: commentLines.join('\n'), - // TODO(scottmg): Use add_labels to add 'User-Submitted' rather than - // duplicating the template's labels (the first two) once - // https://bugs.chromium.org/p/monorail/issues/detail?id=1488 is done. - labels: - 'Restrict-View-EditIssue,Stability-Crash,User-Submitted,Pri-3,Type-Bug', - }; - let href = 'https://bugs.chromium.org/p/chromium/issues/entry'; - for (const param in params) { - href = appendParam(href, param, params[param]!); - } - - window.open(href); -} - /** * Request crashes get uploaded in the background. */ @@ -242,6 +185,27 @@ function requestCrashUpload() { refreshCrashListId = setTimeout(requestCrashes, 5000); } +/** + * Request new log extraction. + */ +function requestNewExtraction() { + chrome.send('requestNewExtraction'); + + // show spinner + getRequiredElement('spinner').hidden = false; + + // Trigger a refresh in 3 seconds. Clear any previous requests. + clearTimeout(refreshCrashListId); + refreshCrashListId = setTimeout(requestCrashes, 3000); +} + +/** + * Request remove all crash files. + */ + function requestClearAll() { + chrome.send('requestClearAll'); +} + /** * Toggles hiding/showing the developer details of a crash report, depending * on the value of the check box. @@ -256,5 +220,7 @@ document.addEventListener('DOMContentLoaded', function() { addWebUiListener('update-crash-list', updateCrashList); getRequiredElement('uploadCrashes').onclick = requestCrashUpload; getRequiredElement('showDevDetails').onclick = toggleDevDetails; + getRequiredElement('clearAll').onclick = requestClearAll; + getRequiredElement('newExtraction').onclick = requestNewExtraction; requestCrashes(); }); diff --git a/components/crash_strings.grdp b/components/crash_strings.grdp --- a/components/crash_strings.grdp +++ b/components/crash_strings.grdp @@ -19,22 +19,22 @@ Status: - Not uploaded + Not saved - Not yet uploaded, or ignored + Not yet saved, or ignored - Upload requested by user + Save requested by user - Uploaded + Saved - Uploaded Crash Report ID: + Saved Crash Report File: - Upload Time: + Saved Time: Local Crash Context: @@ -53,9 +53,15 @@ Crash reporting is disabled. - Start uploading crashes + Start saving crashes - Send now + Save now + + + Clear all + + + Generate report diff --git a/components/minidump_uploader/android/java/src/org/chromium/components/minidump_uploader/CrashFileManager.java b/components/minidump_uploader/android/java/src/org/chromium/components/minidump_uploader/CrashFileManager.java --- a/components/minidump_uploader/android/java/src/org/chromium/components/minidump_uploader/CrashFileManager.java +++ b/components/minidump_uploader/android/java/src/org/chromium/components/minidump_uploader/CrashFileManager.java @@ -109,6 +109,8 @@ public class CrashFileManager { private static final Pattern TMP_PATTERN = Pattern.compile("\\.tmp\\z"); + private static final String SAVED_MINIDUMP_ZIP_SUFFIX = ".zip"; + // The maximum number of non-uploaded crashes that may be kept in the crash reports directory. // Chosen to attempt to balance between keeping a generous number of crashes, and not using up // too much filesystem storage space for obsolete crash reports. @@ -118,7 +120,7 @@ public class CrashFileManager { // The maximum age, in days, considered acceptable for a crash report. Reports older than this // age will be removed. The constant is chosen to be quite conservative, while still allowing // users to eventually reclaim filesystem storage space from obsolete crash reports. - private static final int MAX_CRASH_REPORT_AGE_IN_DAYS = 30; + private static final int MAX_CRASH_REPORT_AGE_IN_DAYS = 5; // The maximum number of non-uploaded crashes to copy to the crash reports directory. The // difference between this value and MAX_CRASH_REPORTS_TO_KEEP is that TO_KEEP is only checked @@ -613,6 +615,9 @@ public class CrashFileManager { && !f.getName().contains(UPLOAD_FORCED_MINIDUMP_SUFFIX)) { continue; } + // as above, zip files must also be excluded + if (f.getName().endsWith(SAVED_MINIDUMP_ZIP_SUFFIX)) + continue; String filenameSansExtension = f.getName().split("\\.")[0]; if (filenameSansExtension.endsWith(localId)) { diff --git a/components/minidump_uploader/android/java/src/org/chromium/components/minidump_uploader/MinidumpUploadCallable.java b/components/minidump_uploader/android/java/src/org/chromium/components/minidump_uploader/MinidumpUploadCallable.java --- a/components/minidump_uploader/android/java/src/org/chromium/components/minidump_uploader/MinidumpUploadCallable.java +++ b/components/minidump_uploader/android/java/src/org/chromium/components/minidump_uploader/MinidumpUploadCallable.java @@ -63,27 +63,8 @@ public class MinidumpUploadCallable implements Callable { if (mPermManager.isUploadEnabledForTests()) { Log.i(TAG, "Minidump upload enabled for tests, skipping other checks."); } else if (!CrashFileManager.isForcedUpload(mFileToUpload)) { - if (!mPermManager.isUsageAndCrashReportingPermitted()) { - Log.i(TAG, - "Minidump upload is not permitted. Marking file as skipped " - + "for cleanup to prevent future uploads."); - CrashFileManager.markUploadSkipped(mFileToUpload); - return MinidumpUploadStatus.USER_DISABLED; - } - - if (!mPermManager.isClientInMetricsSample()) { - Log.i(TAG, "Minidump upload skipped due to sampling. Marking file as skipped for " - + "cleanup to prevent future uploads."); - CrashFileManager.markUploadSkipped(mFileToUpload); - return MinidumpUploadStatus.DISABLED_BY_SAMPLING; - } - - if (!mPermManager.isNetworkAvailableForCrashUploads()) { - Log.i(TAG, "Minidump cannot currently be uploaded due to network constraints."); - return MinidumpUploadStatus.FAILURE; - } + return MinidumpUploadStatus.USER_DISABLED; } - MinidumpUploader.Result result = mMinidumpUploader.upload(mFileToUpload); if (result.isSuccess()) { String uploadId = result.message(); diff --git a/components/minidump_uploader/android/java/src/org/chromium/components/minidump_uploader/MinidumpUploader.java b/components/minidump_uploader/android/java/src/org/chromium/components/minidump_uploader/MinidumpUploader.java --- a/components/minidump_uploader/android/java/src/org/chromium/components/minidump_uploader/MinidumpUploader.java +++ b/components/minidump_uploader/android/java/src/org/chromium/components/minidump_uploader/MinidumpUploader.java @@ -120,31 +120,10 @@ public class MinidumpUploader { if (fileToUpload == null || !fileToUpload.exists()) { return Result.failure("Crash report does not exist"); } - HttpURLConnection connection = - mHttpURLConnectionFactory.createHttpURLConnection(CRASH_URL_STRING); - if (connection == null) { - return Result.failure("Failed to create connection"); - } - configureConnectionForHttpPost(connection, readBoundary(fileToUpload)); - - try (InputStream minidumpInputStream = new FileInputStream(fileToUpload); - OutputStream requestBodyStream = - new GZIPOutputStream(connection.getOutputStream())) { - streamCopy(minidumpInputStream, requestBodyStream); - int responseCode = connection.getResponseCode(); - if (isSuccessful(responseCode)) { - // The crash server returns the crash ID in the response body. - String responseContent = getResponseContentAsString(connection); - String uploadId = responseContent != null ? responseContent : "unknown"; - return Result.success(uploadId); - } else { - // Return the remote error code and message. - return Result.uploadError(responseCode, connection.getResponseMessage()); - } - } finally { - connection.disconnect(); - } - } catch (IOException | RuntimeException e) { + // for us, it's always good + // returns the file name without path, which will be registered as local_id + return Result.success(fileToUpload.getName()); + } catch (RuntimeException e) { return Result.failure(e.toString()); } } diff --git a/components/upload_list/text_log_upload_list.cc b/components/upload_list/text_log_upload_list.cc --- a/components/upload_list/text_log_upload_list.cc +++ b/components/upload_list/text_log_upload_list.cc @@ -165,6 +165,7 @@ std::unique_ptr TextLogUploadList::TryParseCsvLogEntry( } auto info = std::make_unique(components[1], upload_time); + info->file_path = components[1]; // Add local ID if present. if (components.size() > 2) diff --git a/components/upload_list/upload_list.cc b/components/upload_list/upload_list.cc --- a/components/upload_list/upload_list.cc +++ b/components/upload_list/upload_list.cc @@ -99,6 +99,11 @@ std::vector UploadList::GetUploads( return uploads; } +void UploadList::RequestNewExtraction() { + // only available for Android. overrided in crash_upload_list_android.cc + NOTREACHED(); +} + void UploadList::OnLoadComplete( std::vector> uploads) { uploads_ = std::move(uploads); @@ -111,3 +116,12 @@ void UploadList::OnClearComplete() { if (!clear_callback_.is_null()) std::move(clear_callback_).Run(); } + +std::string UploadList::GetFilePathByLocalId(const std::string& local_id) { + for (const std::unique_ptr& info : uploads_) { + if (info->local_id == local_id) { + return info->file_path; + } + } + return std::string(); +} diff --git a/components/upload_list/upload_list.h b/components/upload_list/upload_list.h --- a/components/upload_list/upload_list.h +++ b/components/upload_list/upload_list.h @@ -69,6 +69,9 @@ class UploadList : public base::RefCountedThreadSafe { // Formatted file size for locally stored data. std::u16string file_size; + + // path of crash file + std::string file_path; }; UploadList(); @@ -99,6 +102,12 @@ class UploadList : public base::RefCountedThreadSafe { // The |UploadInfo| pointers are still owned by this |UploadList| instance. std::vector GetUploads(size_t max_count) const; + // Get full path of crash file for local_id + std::string GetFilePathByLocalId(const std::string& local_id); + + // Request new log extraction + virtual void RequestNewExtraction(); + protected: virtual ~UploadList(); -- 2.25.1