Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(10)

Side by Side Diff: chrome/browser/safe_browsing/srt_fetcher_win.cc

Issue 2226133005: Add support for the ExperimentalSwReporterEngine field trial. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Comment on usage of TestPartialLaunchCycle. Always save the time of last run. Misc cleanups. Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/safe_browsing/srt_fetcher_win.h" 5 #include "chrome/browser/safe_browsing/srt_fetcher_win.h"
6 6
7 #include <stdint.h> 7 #include <stdint.h>
8 8
9 #include <memory> 9 #include <memory>
10 #include <utility> 10 #include <utility>
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after
112 112
113 const char kFoundUwsMetricName[] = "SoftwareReporter.FoundUwS"; 113 const char kFoundUwsMetricName[] = "SoftwareReporter.FoundUwS";
114 const char kFoundUwsReadErrorMetricName[] = 114 const char kFoundUwsReadErrorMetricName[] =
115 "SoftwareReporter.FoundUwSReadError"; 115 "SoftwareReporter.FoundUwSReadError";
116 const char kScanTimesMetricName[] = "SoftwareReporter.UwSScanTimes"; 116 const char kScanTimesMetricName[] = "SoftwareReporter.UwSScanTimes";
117 const char kMemoryUsedMetricName[] = "SoftwareReporter.MemoryUsed"; 117 const char kMemoryUsedMetricName[] = "SoftwareReporter.MemoryUsed";
118 118
119 // Reports metrics about the software reporter via UMA (and sometimes Rappor). 119 // Reports metrics about the software reporter via UMA (and sometimes Rappor).
120 class UMAHistogramReporter { 120 class UMAHistogramReporter {
121 public: 121 public:
122 explicit UMAHistogramReporter(const std::string& suffix = std::string()) 122 UMAHistogramReporter() : UMAHistogramReporter(std::string()) {}
123
124 explicit UMAHistogramReporter(const std::string& suffix)
123 : suffix_(suffix), 125 : suffix_(suffix),
124 registry_key_(suffix.empty() ? kSoftwareRemovalToolRegistryKey 126 registry_key_(suffix.empty() ? kSoftwareRemovalToolRegistryKey
125 : base::StringPrintf( 127 : base::StringPrintf(
126 L"%ls\\%ls", 128 L"%ls\\%ls",
127 kSoftwareRemovalToolRegistryKey, 129 kSoftwareRemovalToolRegistryKey,
128 base::UTF8ToUTF16(suffix).c_str())) { 130 base::UTF8ToUTF16(suffix).c_str())) {
129 } 131 }
130 132
131 // Reports the software reporter tool's version via UMA. 133 // Reports the software reporter tool's version via UMA.
132 void ReportVersion(const base::Version& version) const { 134 void ReportVersion(const base::Version& version) const {
(...skipping 442 matching lines...) Expand 10 before | Expand all | Expand 10 after
575 prefs->SetString(prefs::kSwReporterPromptVersion, 577 prefs->SetString(prefs::kSwReporterPromptVersion,
576 reporter_version.GetString()); 578 reporter_version.GetString());
577 579
578 // Download the SRT. 580 // Download the SRT.
579 RecordReporterStepHistogram(SW_REPORTER_DOWNLOAD_START); 581 RecordReporterStepHistogram(SW_REPORTER_DOWNLOAD_START);
580 582
581 // All the work happens in the self-deleting class below. 583 // All the work happens in the self-deleting class below.
582 new SRTFetcher(profile); 584 new SRTFetcher(profile);
583 } 585 }
584 586
585 // This class tries to run the reporter and reacts to its exit code. It 587 // This class tries to run a queue of reporters and react to their exit codes.
586 // schedules subsequent runs as needed, or retries as soon as a browser is 588 // It schedules subsequent runs of the queue as needed, or retries as soon as a
587 // available when none is on first try. 589 // browser is available when none is on first try.
588 class ReporterRunner : public chrome::BrowserListObserver { 590 class ReporterRunner : public chrome::BrowserListObserver {
589 public: 591 public:
590 // Starts the sequence of attempts to run the reporter. 592 // Registers |invocations| to run next time |TryToRun| is scheduled. (And if
591 static void Run(const SwReporterInvocation& invocation, 593 // it's not already scheduled, call it now.)
592 const base::Version& version, 594 static void ScheduleInvocations(
593 scoped_refptr<base::TaskRunner> main_thread_task_runner, 595 const SwReporterQueue& invocations,
594 scoped_refptr<base::TaskRunner> blocking_task_runner) { 596 const base::Version& version,
597 scoped_refptr<base::TaskRunner> main_thread_task_runner,
598 scoped_refptr<base::TaskRunner> blocking_task_runner) {
595 if (!instance_) 599 if (!instance_)
596 instance_ = new ReporterRunner; 600 instance_ = new ReporterRunner;
grt (UTC plus 2) 2016/09/13 06:32:25 since this is intentionally leaked, please use ANN
Joe Mason 2016/09/13 19:24:54 Done.
597 DCHECK_CURRENTLY_ON(BrowserThread::UI); 601 DCHECK_CURRENTLY_ON(BrowserThread::UI);
598 // There's nothing to do if the path and version of the reporter has not 602
599 // changed, we just keep running the tasks that are running now. 603 // There's nothing to do if the invocation parameters and version of the
600 if (instance_->invocation_ == invocation && instance_->version_.IsValid() && 604 // reporter have not changed, we just keep running the tasks that are
601 instance_->version_ == version) 605 // running now.
606 if (instance_->pending_invocations_ == invocations &&
607 instance_->version_.IsValid() && instance_->version_ == version)
602 return; 608 return;
603 609
604 instance_->invocation_ = invocation; 610 instance_->pending_invocations_ = invocations;
605 instance_->version_ = version; 611 instance_->version_ = version;
606 instance_->main_thread_task_runner_ = std::move(main_thread_task_runner); 612 instance_->main_thread_task_runner_ = std::move(main_thread_task_runner);
607 instance_->blocking_task_runner_ = std::move(blocking_task_runner); 613 instance_->blocking_task_runner_ = std::move(blocking_task_runner);
608 614
609 if (instance_->first_run_) { 615 if (instance_->first_run_) {
610 instance_->first_run_ = false; 616 instance_->first_run_ = false;
611 instance_->TryToRun(); 617 instance_->TryToRun();
612 } 618 }
613 } 619 }
614 620
615 private: 621 private:
616 ReporterRunner() {} 622 ReporterRunner() {}
617 ~ReporterRunner() override {} 623 ~ReporterRunner() override {}
618 624
619 // BrowserListObserver. 625 // BrowserListObserver.
620 void OnBrowserSetLastActive(Browser* browser) override {} 626 void OnBrowserSetLastActive(Browser* browser) override {}
621 void OnBrowserRemoved(Browser* browser) override {} 627 void OnBrowserRemoved(Browser* browser) override {}
622 void OnBrowserAdded(Browser* browser) override { 628 void OnBrowserAdded(Browser* browser) override {
623 DCHECK_CURRENTLY_ON(BrowserThread::UI); 629 DCHECK_CURRENTLY_ON(BrowserThread::UI);
624 DCHECK(browser); 630 DCHECK(browser);
625 MaybeFetchSRT(browser, version_); 631 MaybeFetchSRT(browser, version_);
626 BrowserList::RemoveObserver(this); 632 BrowserList::RemoveObserver(this);
627 } 633 }
628 634
629 // This method is called on the UI thread when the reporter run has completed. 635 // Launches the command line at the head of the queue.
630 // This is run as a task posted from an interruptible worker thread so should 636 void ScheduleNextInvocation() {
631 // be resilient to unexpected shutdown. 637 DCHECK(!current_invocations_.empty());
638 auto next_invocation = current_invocations_.front();
639 current_invocations_.pop();
640
641 if (g_testing_delegate_)
642 g_testing_delegate_->NotifyLaunchReady();
643
644 // It's OK to simply |PostTaskAndReplyWithResult| so that
645 // |LaunchAndWaitForExit| doesn't need to access |main_thread_task_runner_|
646 // since the callback is not delayed and the test task runner won't need to
647 // force it.
648 base::PostTaskAndReplyWithResult(
649 blocking_task_runner_.get(), FROM_HERE,
650 base::Bind(&LaunchAndWaitForExit, next_invocation),
651 base::Bind(&ReporterRunner::ReporterDone, base::Unretained(this),
652 base::Time::Now(), version_, next_invocation));
653 }
654
655 // This method is called on the UI thread when an invocation of the reporter
656 // has completed. This is run as a task posted from an interruptible worker
657 // thread so should be resilient to unexpected shutdown.
632 void ReporterDone(const base::Time& reporter_start_time, 658 void ReporterDone(const base::Time& reporter_start_time,
633 const base::Version& version, 659 const base::Version& version,
634 const SwReporterInvocation& finished_invocation, 660 const SwReporterInvocation& finished_invocation,
635 int exit_code) { 661 int exit_code) {
636 DCHECK_CURRENTLY_ON(BrowserThread::UI); 662 DCHECK_CURRENTLY_ON(BrowserThread::UI);
637 663
638 if (g_testing_delegate_) 664 if (g_testing_delegate_)
639 g_testing_delegate_->NotifyReporterDone(); 665 g_testing_delegate_->NotifyReporterDone();
640 666
641 base::TimeDelta reporter_running_time = 667 base::TimeDelta reporter_running_time =
642 base::Time::Now() - reporter_start_time; 668 base::Time::Now() - reporter_start_time;
643 // Don't continue when the reporter process failed to launch, but still try 669
644 // again after the regular delay. It's not worth retrying earlier, risking 670 // Don't continue the current queue of reporters if one failed to launch.
645 // running too often if it always fails, since not many users fail here. 671 if (exit_code == kReporterFailureExitCode)
646 main_thread_task_runner_->PostDelayedTask( 672 current_invocations_ = SwReporterQueue();
647 FROM_HERE, 673
648 base::Bind(&ReporterRunner::TryToRun, base::Unretained(this)), 674 // As soon as we're not running this queue, schedule the next overall queue
649 base::TimeDelta::FromDays(days_between_reporter_runs_)); 675 // run after the regular delay. (If there was a failure it's not worth
676 // retrying earlier, risking running too often if it always fails, since
677 // not many users fail here.)
678 if (current_invocations_.empty()) {
679 main_thread_task_runner_->PostDelayedTask(
680 FROM_HERE,
681 base::Bind(&ReporterRunner::TryToRun, base::Unretained(this)),
682 base::TimeDelta::FromDays(days_between_reporter_runs_));
683 } else {
684 ScheduleNextInvocation();
685 }
686
687 // If the reporter failed to launch, do not process the results. (The exit
688 // code itself doesn't need to be logged in this case because
689 // SW_REPORTER_FAILED_TO_START is logged in |LaunchAndWaitForExit|.)
650 if (exit_code == kReporterFailureExitCode) 690 if (exit_code == kReporterFailureExitCode)
651 return; 691 return;
652 692
653 UMAHistogramReporter uma(finished_invocation.suffix); 693 UMAHistogramReporter uma(finished_invocation.suffix);
654 uma.ReportVersion(version); 694 uma.ReportVersion(version);
655 uma.ReportExitCode(exit_code); 695 uma.ReportExitCode(exit_code);
656 uma.ReportFoundUwS(!finished_invocation.is_experimental /*use_rappor*/); 696 uma.ReportFoundUwS(finished_invocation.flags &
697 SwReporterInvocation::FLAG_LOG_TO_RAPPOR);
657 698
658 // Only save results from the canonical version of the software.
659 PrefService* local_state = g_browser_process->local_state(); 699 PrefService* local_state = g_browser_process->local_state();
660 if (local_state && !finished_invocation.is_experimental) { 700 if (local_state) {
661 local_state->SetInteger(prefs::kSwReporterLastExitCode, exit_code); 701 if (finished_invocation.flags & SwReporterInvocation::FLAG_LOG_TO_PREFS)
csharp 2016/09/13 17:09:03 LOGS_TO_PREFS -> LOG_REPORTER_LAST_EXIT_CODE? Sinc
Joe Mason 2016/09/13 19:24:55 Done.
702 local_state->SetInteger(prefs::kSwReporterLastExitCode, exit_code);
662 local_state->SetInt64(prefs::kSwReporterLastTimeTriggered, 703 local_state->SetInt64(prefs::kSwReporterLastTimeTriggered,
663 base::Time::Now().ToInternalValue()); 704 base::Time::Now().ToInternalValue());
664 } 705 }
665 uma.ReportRuntime(reporter_running_time); 706 uma.ReportRuntime(reporter_running_time);
666 uma.ReportScanTimes(); 707 uma.ReportScanTimes();
667 uma.ReportMemoryUsage(); 708 uma.ReportMemoryUsage();
668 709
669 // Only continue to launch the prompt for the canonical version. 710 if (!(finished_invocation.flags &
670 if (finished_invocation.is_experimental) 711 SwReporterInvocation::FLAG_TRIGGER_PROMPT))
671 return; 712 return;
672 713
673 if (!IsInSRTPromptFieldTrialGroups()) { 714 if (!IsInSRTPromptFieldTrialGroups()) {
674 // Knowing about disabled field trial is more important than reporter not 715 // Knowing about disabled field trial is more important than reporter not
675 // finding anything to remove, so check this case first. 716 // finding anything to remove, so check this case first.
676 RecordReporterStepHistogram(SW_REPORTER_NO_PROMPT_FIELD_TRIAL); 717 RecordReporterStepHistogram(SW_REPORTER_NO_PROMPT_FIELD_TRIAL);
677 return; 718 return;
678 } 719 }
679 720
680 if (exit_code != kSwReporterPostRebootCleanupNeeded && 721 if (exit_code != kSwReporterPostRebootCleanupNeeded &&
(...skipping 14 matching lines...) Expand all
695 BrowserList::AddObserver(this); 736 BrowserList::AddObserver(this);
696 } else { 737 } else {
697 MaybeFetchSRT(browser, version_); 738 MaybeFetchSRT(browser, version_);
698 } 739 }
699 } 740 }
700 741
701 void TryToRun() { 742 void TryToRun() {
702 DCHECK_CURRENTLY_ON(BrowserThread::UI); 743 DCHECK_CURRENTLY_ON(BrowserThread::UI);
703 PrefService* local_state = g_browser_process->local_state(); 744 PrefService* local_state = g_browser_process->local_state();
704 if (!version_.IsValid() || !local_state) { 745 if (!version_.IsValid() || !local_state) {
705 DCHECK(first_run_); 746 DCHECK(first_run_);
grt (UTC plus 2) 2016/09/13 06:32:25 this is guaranteed to DCHECK if the condition abov
Joe Mason 2016/09/13 19:24:54 I think that's the intent, but I'm not sure how th
grt (UTC plus 2) 2016/09/13 19:47:22 Ack, though the code here doesn't make sense. Plea
Joe Mason 2016/09/13 21:34:42 Done.
706 return; 747 return;
707 } 748 }
708 749
709 // Run the reporter if it hasn't been triggered in the last 750 // Run a queue of reporters if none have been triggered in the last
710 // |days_between_reporter_runs_| days, which depends if there is a pending 751 // |days_between_reporter_runs_| days, which depends if there is a pending
711 // prompt to be added to Chrome's menu. 752 // prompt to be added to Chrome's menu.
712 if (local_state->GetBoolean(prefs::kSwReporterPendingPrompt)) { 753 if (local_state->GetBoolean(prefs::kSwReporterPendingPrompt)) {
713 days_between_reporter_runs_ = kDaysBetweenSwReporterRunsForPendingPrompt; 754 days_between_reporter_runs_ = kDaysBetweenSwReporterRunsForPendingPrompt;
714 RecordReporterStepHistogram(SW_REPORTER_RAN_DAILY); 755 RecordReporterStepHistogram(SW_REPORTER_RAN_DAILY);
715 } else { 756 } else {
716 days_between_reporter_runs_ = kDaysBetweenSuccessfulSwReporterRuns; 757 days_between_reporter_runs_ = kDaysBetweenSuccessfulSwReporterRuns;
717 } 758 }
759 const base::Time now = base::Time::Now();
718 const base::Time last_time_triggered = base::Time::FromInternalValue( 760 const base::Time last_time_triggered = base::Time::FromInternalValue(
719 local_state->GetInt64(prefs::kSwReporterLastTimeTriggered)); 761 local_state->GetInt64(prefs::kSwReporterLastTimeTriggered));
720 base::TimeDelta next_trigger_delay( 762 const base::Time next_trigger(
721 last_time_triggered + 763 last_time_triggered +
722 base::TimeDelta::FromDays(days_between_reporter_runs_) - 764 base::TimeDelta::FromDays(days_between_reporter_runs_));
723 base::Time::Now()); 765 if (!pending_invocations_.empty() &&
724 if (next_trigger_delay.ToInternalValue() <= 0 || 766 (next_trigger <= now ||
725 // Also make sure the kSwReporterLastTimeTriggered value is not set in 767 // Also make sure the kSwReporterLastTimeTriggered value is not set in
726 // the future. 768 // the future.
727 last_time_triggered > base::Time::Now()) { 769 last_time_triggered > now)) {
728 if (g_testing_delegate_) 770 DCHECK(current_invocations_.empty());
729 g_testing_delegate_->NotifyLaunchReady(); 771 current_invocations_ = pending_invocations_;
730 772 ScheduleNextInvocation();
731 // It's OK to simply |PostTaskAndReplyWithResult| so that
732 // |LaunchAndWaitForExit| doesn't need to access
733 // |main_thread_task_runner_| since the callback is not delayed and the
734 // test task runner won't need to force it.
735 base::PostTaskAndReplyWithResult(
736 blocking_task_runner_.get(), FROM_HERE,
737 base::Bind(&LaunchAndWaitForExit, invocation_),
738 base::Bind(&ReporterRunner::ReporterDone, base::Unretained(this),
739 base::Time::Now(), version_, invocation_));
740 } else { 773 } else {
741 main_thread_task_runner_->PostDelayedTask( 774 main_thread_task_runner_->PostDelayedTask(
742 FROM_HERE, 775 FROM_HERE,
743 base::Bind(&ReporterRunner::TryToRun, base::Unretained(this)), 776 base::Bind(&ReporterRunner::TryToRun, base::Unretained(this)),
744 next_trigger_delay); 777 next_trigger - now);
745 } 778 }
746 } 779 }
747 780
748 bool first_run_ = true; 781 bool first_run_ = true;
749 SwReporterInvocation invocation_; 782
783 // The queue of invocations that are currently running.
784 SwReporterQueue current_invocations_;
785
786 // The invocations to run next time the SwReporter is run.
787 SwReporterQueue pending_invocations_;
788
750 base::Version version_; 789 base::Version version_;
751 scoped_refptr<base::TaskRunner> main_thread_task_runner_; 790 scoped_refptr<base::TaskRunner> main_thread_task_runner_;
752 scoped_refptr<base::TaskRunner> blocking_task_runner_; 791 scoped_refptr<base::TaskRunner> blocking_task_runner_;
753 792
754 // This value is used to identify how long to wait before starting a new run 793 // This value is used to identify how long to wait before starting a new run
755 // of the reporter. It's initialized with the default value and may be changed 794 // of the reporter queue. It's initialized with the default value and may be
756 // to a different value when a prompt is pending and the reporter should be 795 // changed to a different value when a prompt is pending and the reporter
757 // run before adding the global error to the Chrome menu. 796 // should be run before adding the global error to the Chrome menu.
758 int days_between_reporter_runs_ = kDaysBetweenSuccessfulSwReporterRuns; 797 int days_between_reporter_runs_ = kDaysBetweenSuccessfulSwReporterRuns;
759 798
760 // A single leaky instance. 799 // A single leaky instance.
761 static ReporterRunner* instance_; 800 static ReporterRunner* instance_;
762 801
763 DISALLOW_COPY_AND_ASSIGN(ReporterRunner); 802 DISALLOW_COPY_AND_ASSIGN(ReporterRunner);
764 }; 803 };
765 804
766 ReporterRunner* ReporterRunner::instance_ = nullptr; 805 ReporterRunner* ReporterRunner::instance_ = nullptr;
767 806
(...skipping 11 matching lines...) Expand all
779 818
780 SwReporterInvocation SwReporterInvocation::FromCommandLine( 819 SwReporterInvocation SwReporterInvocation::FromCommandLine(
781 const base::CommandLine& command_line) { 820 const base::CommandLine& command_line) {
782 SwReporterInvocation invocation; 821 SwReporterInvocation invocation;
783 invocation.command_line = command_line; 822 invocation.command_line = command_line;
784 return invocation; 823 return invocation;
785 } 824 }
786 825
787 bool SwReporterInvocation::operator==(const SwReporterInvocation& other) const { 826 bool SwReporterInvocation::operator==(const SwReporterInvocation& other) const {
788 return command_line.argv() == other.command_line.argv() && 827 return command_line.argv() == other.command_line.argv() &&
789 suffix == other.suffix && is_experimental == other.is_experimental; 828 suffix == other.suffix && flags == other.flags;
790 } 829 }
791 830
792 void RunSwReporter(const SwReporterInvocation& invocation, 831 void RunSwReporters(const SwReporterQueue& invocations,
793 const base::Version& version, 832 const base::Version& version,
794 scoped_refptr<base::TaskRunner> main_thread_task_runner, 833 scoped_refptr<base::TaskRunner> main_thread_task_runner,
795 scoped_refptr<base::TaskRunner> blocking_task_runner) { 834 scoped_refptr<base::TaskRunner> blocking_task_runner) {
796 ReporterRunner::Run(invocation, version, std::move(main_thread_task_runner), 835 DCHECK(!invocations.empty());
797 std::move(blocking_task_runner)); 836 ReporterRunner::ScheduleInvocations(invocations, version,
837 std::move(main_thread_task_runner),
838 std::move(blocking_task_runner));
798 } 839 }
799 840
800 bool ReporterFoundUws() { 841 bool ReporterFoundUws() {
801 PrefService* local_state = g_browser_process->local_state(); 842 PrefService* local_state = g_browser_process->local_state();
802 if (!local_state) 843 if (!local_state)
803 return false; 844 return false;
804 int exit_code = local_state->GetInteger(prefs::kSwReporterLastExitCode); 845 int exit_code = local_state->GetInteger(prefs::kSwReporterLastExitCode);
805 return exit_code == kSwReporterCleanupNeeded; 846 return exit_code == kSwReporterCleanupNeeded;
806 } 847 }
807 848
808 bool UserHasRunCleaner() { 849 bool UserHasRunCleaner() {
809 base::string16 cleaner_key_path(kSoftwareRemovalToolRegistryKey); 850 base::string16 cleaner_key_path(kSoftwareRemovalToolRegistryKey);
810 cleaner_key_path.append(L"\\").append(kCleanerSubKey); 851 cleaner_key_path.append(L"\\").append(kCleanerSubKey);
811 852
812 base::win::RegKey srt_cleaner_key(HKEY_CURRENT_USER, cleaner_key_path.c_str(), 853 base::win::RegKey srt_cleaner_key(HKEY_CURRENT_USER, cleaner_key_path.c_str(),
813 KEY_QUERY_VALUE); 854 KEY_QUERY_VALUE);
814 855
815 return srt_cleaner_key.Valid() && srt_cleaner_key.GetValueCount() > 0; 856 return srt_cleaner_key.Valid() && srt_cleaner_key.GetValueCount() > 0;
816 } 857 }
817 858
818 void SetSwReporterTestingDelegate(SwReporterTestingDelegate* delegate) { 859 void SetSwReporterTestingDelegate(SwReporterTestingDelegate* delegate) {
819 g_testing_delegate_ = delegate; 860 g_testing_delegate_ = delegate;
820 } 861 }
821 862
822 } // namespace safe_browsing 863 } // namespace safe_browsing
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698