summaryrefslogtreecommitdiff
path: root/chromium/base/metrics/histogram_snapshot_manager.cc
blob: e21d512daba271dc43248a9e63b598ce98d55472 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/metrics/histogram_snapshot_manager.h"

#include <memory>

#include "base/debug/alias.h"
#include "base/metrics/histogram_flattener.h"
#include "base/metrics/histogram_samples.h"
#include "base/metrics/statistics_recorder.h"
#include "base/stl_util.h"

namespace base {

HistogramSnapshotManager::HistogramSnapshotManager(
    HistogramFlattener* histogram_flattener)
    : preparing_deltas_(false),
      histogram_flattener_(histogram_flattener) {
  DCHECK(histogram_flattener_);
}

HistogramSnapshotManager::~HistogramSnapshotManager() {
}

void HistogramSnapshotManager::StartDeltas() {
  // Ensure that start/finish calls do not get nested.
  DCHECK(!preparing_deltas_);
  preparing_deltas_ = true;

  DCHECK(owned_histograms_.empty());

#ifdef DEBUG
    CHECK(!iter->second.histogram);
    CHECK(!iter->second.accumulated_samples);
    CHECK(!(iter->second.inconsistencies &
            HistogramBase::NEW_INCONSISTENCY_FOUND));
  }
#endif
}

void HistogramSnapshotManager::PrepareDelta(HistogramBase* histogram) {
  PrepareSamples(histogram, histogram->SnapshotDelta());
}

void HistogramSnapshotManager::PrepareDeltaTakingOwnership(
    std::unique_ptr<HistogramBase> histogram) {
  PrepareSamples(histogram.get(), histogram->SnapshotDelta());
  owned_histograms_.push_back(std::move(histogram));
}

void HistogramSnapshotManager::PrepareAbsolute(const HistogramBase* histogram) {
  PrepareSamples(histogram, histogram->SnapshotSamples());
}

void HistogramSnapshotManager::PrepareAbsoluteTakingOwnership(
    std::unique_ptr<const HistogramBase> histogram) {
  PrepareSamples(histogram.get(), histogram->SnapshotSamples());
  owned_histograms_.push_back(std::move(histogram));
}

void HistogramSnapshotManager::FinishDeltas() {
  DCHECK(preparing_deltas_);

  // Iterate over all known histograms to see what should be recorded.
  for (auto& hash_and_info : known_histograms_) {
    SampleInfo* sample_info = &hash_and_info.second;

    // First, record any histograms in which corruption was detected.
    if (sample_info->inconsistencies & HistogramBase::NEW_INCONSISTENCY_FOUND) {
      sample_info->inconsistencies &= ~HistogramBase::NEW_INCONSISTENCY_FOUND;
      histogram_flattener_->UniqueInconsistencyDetected(
          static_cast<HistogramBase::Inconsistency>(
              sample_info->inconsistencies));
    }

    // Second, record actual accumulated deltas.
    if (sample_info->accumulated_samples) {
      // TODO(bcwhite): Investigate using redundant_count() below to avoid
      // additional pass through all the samples to calculate real total.
      if (sample_info->accumulated_samples->TotalCount() > 0) {
        histogram_flattener_->RecordDelta(*sample_info->histogram,
                                          *sample_info->accumulated_samples);
      }
      delete sample_info->accumulated_samples;
      sample_info->accumulated_samples = nullptr;
    }

    // The Histogram pointer must be cleared at this point because the owner
    // is only required to keep it alive until FinishDeltas() completes.
    sample_info->histogram = nullptr;
  }

  owned_histograms_.clear();
  preparing_deltas_ = false;
}

void HistogramSnapshotManager::PrepareSamples(
    const HistogramBase* histogram,
    std::unique_ptr<HistogramSamples> samples) {
  DCHECK(histogram_flattener_);

  // Get information known about this histogram.
  SampleInfo* sample_info = &known_histograms_[histogram->name_hash()];
  if (sample_info->histogram) {
    DCHECK_EQ(sample_info->histogram->histogram_name(),
              histogram->histogram_name()) << "hash collision";
  } else {
    // First time this histogram has been seen; datafill.
    sample_info->histogram = histogram;
  }

  // Crash if we detect that our histograms have been overwritten.  This may be
  // a fair distance from the memory smasher, but we hope to correlate these
  // crashes with other events, such as plugins, or usage patterns, etc.
  uint32_t corruption = histogram->FindCorruption(*samples);
  if (HistogramBase::BUCKET_ORDER_ERROR & corruption) {
    // The checksum should have caught this, so crash separately if it didn't.
    CHECK_NE(0U, HistogramBase::RANGE_CHECKSUM_ERROR & corruption);
    CHECK(false);  // Crash for the bucket order corruption.
    // Ensure that compiler keeps around pointers to |histogram| and its
    // internal |bucket_ranges_| for any minidumps.
    base::debug::Alias(
        static_cast<const Histogram*>(histogram)->bucket_ranges());
  }
  // Checksum corruption might not have caused order corruption.
  CHECK_EQ(0U, HistogramBase::RANGE_CHECKSUM_ERROR & corruption);

  // Note, at this point corruption can only be COUNT_HIGH_ERROR or
  // COUNT_LOW_ERROR and they never arise together, so we don't need to extract
  // bits from corruption.
  if (corruption) {
    DLOG(ERROR) << "Histogram: \"" << histogram->histogram_name()
                << "\" has data corruption: " << corruption;
    histogram_flattener_->InconsistencyDetected(
        static_cast<HistogramBase::Inconsistency>(corruption));
    // Don't record corrupt data to metrics services.
    const uint32_t old_corruption = sample_info->inconsistencies;
    if (old_corruption == (corruption | old_corruption))
      return;  // We've already seen this corruption for this histogram.
    sample_info->inconsistencies |=
        corruption | HistogramBase::NEW_INCONSISTENCY_FOUND;
    // TODO(bcwhite): Can we clear the inconsistency for future collection?
    return;
  }

  if (!sample_info->accumulated_samples) {
    // This histogram has not been seen before; add it as a new entry.
    sample_info->accumulated_samples = samples.release();
  } else {
    // There are previous values from this histogram; add them together.
    sample_info->accumulated_samples->Add(*samples);
  }
}

void HistogramSnapshotManager::InspectLoggedSamplesInconsistency(
      const HistogramSamples& new_snapshot,
      HistogramSamples* logged_samples) {
  HistogramBase::Count discrepancy =
      logged_samples->TotalCount() - logged_samples->redundant_count();
  if (!discrepancy)
    return;

  histogram_flattener_->InconsistencyDetectedInLoggedCount(discrepancy);
  if (discrepancy > Histogram::kCommonRaceBasedCountMismatch) {
    // Fix logged_samples.
    logged_samples->Subtract(*logged_samples);
    logged_samples->Add(new_snapshot);
  }
}

}  // namespace base