1// Copyright 2014 the V8 project 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
5#include "src/diagnostics/compilation-statistics.h"
6
7#include <ostream>
8#include <vector>
9
10#include "src/base/platform/platform.h"
11
12namespace v8 {
13namespace internal {
14
15void CompilationStatistics::RecordPhaseStats(const char* phase_kind_name,
16                                             const char* phase_name,
17                                             const BasicStats& stats) {
18  base::MutexGuard guard(&record_mutex_);
19
20  std::string phase_name_str(phase_name);
21  auto it = phase_map_.find(phase_name_str);
22  if (it == phase_map_.end()) {
23    PhaseStats phase_stats(phase_map_.size(), phase_kind_name);
24    it = phase_map_.insert(std::make_pair(phase_name_str, phase_stats)).first;
25  }
26  it->second.Accumulate(stats);
27}
28
29void CompilationStatistics::RecordPhaseKindStats(const char* phase_kind_name,
30                                                 const BasicStats& stats) {
31  base::MutexGuard guard(&record_mutex_);
32
33  std::string phase_kind_name_str(phase_kind_name);
34  auto it = phase_kind_map_.find(phase_kind_name_str);
35  if (it == phase_kind_map_.end()) {
36    PhaseKindStats phase_kind_stats(phase_kind_map_.size());
37    it = phase_kind_map_
38             .insert(std::make_pair(phase_kind_name_str, phase_kind_stats))
39             .first;
40  }
41  it->second.Accumulate(stats);
42}
43
44void CompilationStatistics::RecordTotalStats(const BasicStats& stats) {
45  base::MutexGuard guard(&record_mutex_);
46  total_stats_.Accumulate(stats);
47}
48
49void CompilationStatistics::BasicStats::Accumulate(const BasicStats& stats) {
50  delta_ += stats.delta_;
51  total_allocated_bytes_ += stats.total_allocated_bytes_;
52  if (stats.absolute_max_allocated_bytes_ > absolute_max_allocated_bytes_) {
53    absolute_max_allocated_bytes_ = stats.absolute_max_allocated_bytes_;
54    max_allocated_bytes_ = stats.max_allocated_bytes_;
55    function_name_ = stats.function_name_;
56  }
57}
58
59std::string CompilationStatistics::BasicStats::AsJSON() {
60// clang-format off
61#define DICT(s) "{" << s << "}"
62#define QUOTE(s) "\"" << s << "\""
63#define MEMBER(s) QUOTE(s) << ":"
64
65  DCHECK_EQ(function_name_.find("\""), std::string::npos);
66
67  std::stringstream stream;
68  stream << DICT(
69    MEMBER("function_name") << QUOTE(function_name_) << ","
70    MEMBER("total_allocated_bytes") << total_allocated_bytes_ << ","
71    MEMBER("max_allocated_bytes") << max_allocated_bytes_ << ","
72    MEMBER("absolute_max_allocated_bytes") << absolute_max_allocated_bytes_);
73
74  return stream.str();
75
76#undef DICT
77#undef QUOTE
78#undef MEMBER
79  // clang-format on
80}
81
82static void WriteLine(std::ostream& os, bool machine_format, const char* name,
83                      const CompilationStatistics::BasicStats& stats,
84                      const CompilationStatistics::BasicStats& total_stats) {
85  const size_t kBufferSize = 128;
86  char buffer[kBufferSize];
87
88  double ms = stats.delta_.InMillisecondsF();
89  double percent = stats.delta_.PercentOf(total_stats.delta_);
90  double size_percent =
91      static_cast<double>(stats.total_allocated_bytes_ * 100) /
92      static_cast<double>(total_stats.total_allocated_bytes_);
93  if (machine_format) {
94    base::OS::SNPrintF(buffer, kBufferSize,
95                       "\"%s_time\"=%.3f\n\"%s_space\"=%zu", name, ms, name,
96                       stats.total_allocated_bytes_);
97    os << buffer;
98  } else {
99    base::OS::SNPrintF(buffer, kBufferSize,
100                       "%34s %10.3f (%5.1f%%)  %10zu (%5.1f%%) %10zu %10zu",
101                       name, ms, percent, stats.total_allocated_bytes_,
102                       size_percent, stats.max_allocated_bytes_,
103                       stats.absolute_max_allocated_bytes_);
104
105    os << buffer;
106    if (!stats.function_name_.empty()) {
107      os << "   " << stats.function_name_.c_str();
108    }
109    os << std::endl;
110  }
111}
112
113static void WriteFullLine(std::ostream& os) {
114  os << "-----------------------------------------------------------"
115        "-----------------------------------------------------------\n";
116}
117
118static void WriteHeader(std::ostream& os) {
119  WriteFullLine(os);
120  os << "                Turbofan phase            Time (ms)    "
121     << "                   Space (bytes)             Function\n"
122     << "                                                       "
123     << "          Total          Max.     Abs. max.\n";
124  WriteFullLine(os);
125}
126
127static void WritePhaseKindBreak(std::ostream& os) {
128  os << "                                   ------------------------"
129        "-----------------------------------------------------------\n";
130}
131
132std::ostream& operator<<(std::ostream& os, const AsPrintableStatistics& ps) {
133  // phase_kind_map_ and phase_map_ don't get mutated, so store a bunch of
134  // pointers into them.
135  const CompilationStatistics& s = ps.s;
136
137  using SortedPhaseKinds =
138      std::vector<CompilationStatistics::PhaseKindMap::const_iterator>;
139  SortedPhaseKinds sorted_phase_kinds(s.phase_kind_map_.size());
140  for (auto it = s.phase_kind_map_.begin(); it != s.phase_kind_map_.end();
141       ++it) {
142    sorted_phase_kinds[it->second.insert_order_] = it;
143  }
144
145  using SortedPhases =
146      std::vector<CompilationStatistics::PhaseMap::const_iterator>;
147  SortedPhases sorted_phases(s.phase_map_.size());
148  for (auto it = s.phase_map_.begin(); it != s.phase_map_.end(); ++it) {
149    sorted_phases[it->second.insert_order_] = it;
150  }
151
152  if (!ps.machine_output) WriteHeader(os);
153  for (const auto& phase_kind_it : sorted_phase_kinds) {
154    const auto& phase_kind_name = phase_kind_it->first;
155    if (!ps.machine_output) {
156      for (const auto& phase_it : sorted_phases) {
157        const auto& phase_stats = phase_it->second;
158        if (phase_stats.phase_kind_name_ != phase_kind_name) continue;
159        const auto& phase_name = phase_it->first;
160        WriteLine(os, ps.machine_output, phase_name.c_str(), phase_stats,
161                  s.total_stats_);
162      }
163      WritePhaseKindBreak(os);
164    }
165    const auto& phase_kind_stats = phase_kind_it->second;
166    WriteLine(os, ps.machine_output, phase_kind_name.c_str(), phase_kind_stats,
167              s.total_stats_);
168    os << std::endl;
169  }
170
171  if (!ps.machine_output) WriteFullLine(os);
172  WriteLine(os, ps.machine_output, "totals", s.total_stats_, s.total_stats_);
173
174  return os;
175}
176
177}  // namespace internal
178}  // namespace v8
179