aboutsummaryrefslogtreecommitdiff
path: root/src/metrics.h
blob: 48f26d1b2b5cf32ddeb5a85b998104fa68fe9ac5 (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
// Copyright 2011 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef NINJA_METRICS_H_
#define NINJA_METRICS_H_

#include <atomic>
#include <mutex>
#include <string>
#include <vector>
using namespace std;

#include "util.h"  // For int64_t.

struct Status;

/// The Metrics module is used for the debug mode that dumps timing stats of
/// various actions.  To use, see METRIC_RECORD below.

/// A single metrics we're tracking, like "depfile load time".
struct Metric {
  Metric(const std::string& name) : name_(name) {}

  void AddResult(int count, int64_t time_us) {
    thread_local size_t this_slot = GetThreadSlotIndex();
    Slot& slot = slots_[this_slot];
    slot.count += count;
    slot.time += time_us;
  }

  const std::string& name() const { return name_; }

  int count() const {
    int result = 0;
    for (const Slot& slot : slots_) {
      result += slot.count;
    }
    return result;
  }

  int64_t time() const {
    int64_t result = 0;
    for (const Slot& slot : slots_) {
      result += slot.time;
    }
    return result;
  }

private:
  /// Try to give each thread a different slot to reduce thread contention.
  struct NINJA_ALIGNAS_CACHE_LINE Slot {
    /// Number of times we've hit the code path.
    std::atomic<int> count {};
    /// Total time (in micros) we've spent on the code path.
    std::atomic<int64_t> time {};
  };

  std::string name_;
  std::vector<Slot> slots_ { GetThreadSlotCount() };
};

/// A scoped object for recording a metric across the body of a function.
/// Used by the METRIC_RECORD macro. Inline the metric_ null check to minimize
/// the effect on the typical case where metrics are disabled.
struct ScopedMetric {
  explicit ScopedMetric(Metric* metric) : metric_(metric) {
    if (metric_ == nullptr) {
      return;
    }
    RecordStart();
  }

  ~ScopedMetric() {
    if (metric_ == nullptr) {
      return;
    }
    RecordResult();
  }

private:
  void RecordStart();
  void RecordResult();

  Metric* metric_;
  /// Timestamp when the measurement started.
  /// Value is platform-dependent.
  int64_t start_;
};

/// The singleton that stores metrics and prints the report.
struct Metrics {
  Metric* NewMetric(const string& name);

  /// Print a summary report to stdout.
  void Report(Status *status);

private:
  std::mutex mutex_;
  vector<Metric*> metrics_;
};

/// Get the current time as relative to some epoch.
/// Epoch varies between platforms; only useful for measuring elapsed time.
int64_t GetTimeMillis();

/// A simple stopwatch which returns the time
/// in seconds since Restart() was called.
struct Stopwatch {
 public:
  Stopwatch() : started_(0) {}

  /// Seconds since Restart() call.
  double Elapsed() const {
    return 1e-6 * static_cast<double>(Now() - started_);
  }

  void Restart() { started_ = Now(); }

 private:
  uint64_t started_;
  uint64_t Now() const;
};

/// The primary interface to metrics.  Use METRIC_RECORD("foobar") at the top
/// of a function to get timing stats recorded for each call of the function.
#define METRIC_RECORD(name)                                             \
  static Metric* metrics_h_metric =                                     \
      g_metrics ? g_metrics->NewMetric(name) : NULL;                    \
  ScopedMetric metrics_h_scoped(metrics_h_metric);

extern Metrics* g_metrics;

void DumpMemoryUsage(Status *status);

#endif // NINJA_METRICS_H_