Coverage Report

Created: 2025-12-12 07:27

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/hermes/lib/VM/Profiler/ChromeTraceSerializer.h
Line
Count
Source
1
/*
2
 * Copyright (c) Meta Platforms, Inc. and affiliates.
3
 *
4
 * This source code is licensed under the MIT license found in the
5
 * LICENSE file in the root directory of this source tree.
6
 */
7
8
#ifndef HERMES_VM_PROFILER_CHROMETRACESERIALIZER_H
9
#define HERMES_VM_PROFILER_CHROMETRACESERIALIZER_H
10
11
#include "hermes/VM/Profiler/SamplingProfilerDefs.h"
12
13
#if HERMESVM_SAMPLING_PROFILER_AVAILABLE
14
// TODO: Remove dependency on SamplingProfilerPosix from ChromeTraceSerializer.
15
// A new header may need to be introduced for data entities. It may make sense
16
// to share the data entity across different SamplingProfiler implementations.
17
18
/// This file convert sampled stack frames into Chrome trace format which
19
/// is documented here:
20
/// https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview
21
22
#include "hermes/Support/JSONEmitter.h"
23
#include "hermes/VM/Profiler/SamplingProfiler.h"
24
25
#include "llvh/ADT/DenseMap.h"
26
27
#include <thread>
28
29
namespace hermes {
30
namespace vm {
31
32
/// Generating next id for stack frame.
33
class ChromeFrameIdGenerator {
34
  uint32_t nextFrameId_{1};
35
36
 public:
37
0
  uint32_t getNextFrameNodeId() {
38
0
    return nextFrameId_++;
39
0
  }
40
};
41
42
/// Represent a single stack frame node in collapsed/merged call tree
43
/// in chrome trace format.
44
class ChromeStackFrameNode {
45
 private:
46
  /// Unique id for the stack frame.
47
  uint32_t id_;
48
  /// Frame information. Is None iff this is the root node.
49
  llvh::Optional<SamplingProfiler::StackFrame> frameInfo_;
50
  /// All callee/children of this stack frame.
51
  std::vector<std::shared_ptr<ChromeStackFrameNode>> children_;
52
  /// How many times was this node on the top of the callstack during profiling.
53
  uint32_t hitCount_ = 0;
54
55
  /// \p node represents the current visiting node.
56
  /// \p parent can be nullptr for root node.
57
  using DfsWalkCallback = const std::function<void(
58
      const ChromeStackFrameNode &node,
59
      const ChromeStackFrameNode *parent)>;
60
61
 private:
62
  void dfsWalkHelper(
63
      DfsWalkCallback &callback,
64
0
      const ChromeStackFrameNode *parent) const {
65
0
    callback(*this, parent);
66
0
    for (const auto &child : children_) {
67
0
      child->dfsWalkHelper(callback, this);
68
0
    }
69
0
  }
70
71
 public:
72
  explicit ChromeStackFrameNode(
73
      uint32_t nextFrameId,
74
      llvh::Optional<SamplingProfiler::StackFrame> frame)
75
0
      : id_(nextFrameId), frameInfo_(frame) {}
76
77
0
  uint32_t getId() const {
78
0
    return id_;
79
0
  }
80
81
  // Get the frame info for this node. Should never be called on the root node
82
  // since it will be None.
83
0
  const SamplingProfiler::StackFrame &getFrameInfo() const {
84
0
    return *frameInfo_;
85
0
  }
86
87
  /// Increments this node's hit counter by one.
88
0
  void addHit() {
89
0
    ++hitCount_;
90
0
  }
91
92
  /// \return this node's hit counter.
93
0
  uint32_t getHitCount() const {
94
0
    return hitCount_;
95
0
  }
96
97
  /// Find a child node matching \p target, otherwise add \p target
98
  /// as a new child.
99
  /// \return the found/added child node.
100
  std::shared_ptr<ChromeStackFrameNode> findOrAddNewChild(
101
      ChromeFrameIdGenerator &frameIdGen,
102
      const SamplingProfiler::StackFrame &target);
103
104
  /// DFS walk the call tree using current node as root.
105
  /// For each visited node, invoke \p callback.
106
0
  void dfsWalk(DfsWalkCallback &callback) const {
107
0
    this->dfsWalkHelper(callback, nullptr);
108
0
  }
109
110
  /// Get the vector with the this node's children list.
111
  const std::vector<std::shared_ptr<ChromeStackFrameNode>> &getChildren()
112
0
      const {
113
0
    return children_;
114
0
  }
115
};
116
117
/// Represent an OS sample event(without duration) in chrome format.
118
class ChromeSampleEvent {
119
 private:
120
  // TODO: get real cpu id.
121
  int cpu_{-1};
122
  // Seems should always be one.
123
  int weight_{1};
124
  SamplingProfiler::ThreadId tid_;
125
  SamplingProfiler::TimeStampType timeStamp_;
126
  std::shared_ptr<ChromeStackFrameNode> leafNode_;
127
128
 public:
129
  explicit ChromeSampleEvent(
130
      SamplingProfiler::ThreadId tid,
131
      SamplingProfiler::TimeStampType timeStamp,
132
      std::shared_ptr<ChromeStackFrameNode> leaf)
133
0
      : tid_(tid), timeStamp_(timeStamp), leafNode_(leaf) {}
134
135
  /// \return CPU id.
136
0
  int getCpu() const {
137
0
    return cpu_;
138
0
  }
139
140
  /// \return weight.
141
0
  int getWeight() const {
142
0
    return weight_;
143
0
  }
144
145
  /// Thread id of this event.
146
0
  SamplingProfiler::ThreadId getTid() const {
147
0
    return tid_;
148
0
  }
149
150
  /// Timestamp when this event occurred.
151
0
  SamplingProfiler::TimeStampType getTimeStamp() const {
152
0
    return timeStamp_;
153
0
  }
154
155
  /// \return leaf frame of the stack in call tree corresponding to
156
  /// this instant sample event.
157
0
  std::shared_ptr<ChromeStackFrameNode> getLeafNode() const {
158
0
    return leafNode_;
159
0
  }
160
};
161
162
/// Represent all data for a trace session in chrome trace format.
163
class ChromeTraceFormat {
164
 private:
165
  /// Id of target process.
166
  uint32_t pid_;
167
  /// Thread names map.
168
  SamplingProfiler::ThreadNamesMap threadNames_;
169
  /// The root of the stack frame tree.
170
  const std::shared_ptr<ChromeStackFrameNode> root_;
171
  /// Maintain all transformed chrome sample events.
172
  std::vector<ChromeSampleEvent> sampleEvents_;
173
174
  explicit ChromeTraceFormat(
175
      uint32_t pid,
176
      const SamplingProfiler::ThreadNamesMap &threadNames,
177
      std::unique_ptr<ChromeStackFrameNode> root)
178
0
      : pid_(pid), threadNames_(threadNames), root_(std::move(root)) {}
179
180
 public:
181
  static ChromeTraceFormat create(
182
      uint32_t pid,
183
      const SamplingProfiler::ThreadNamesMap &threadNames,
184
      const std::vector<SamplingProfiler::StackTrace> &sampledStacks);
185
186
0
  uint32_t getPid() const {
187
0
    return pid_;
188
0
  }
189
190
0
  const SamplingProfiler::ThreadNamesMap &getThreadNames() const {
191
0
    return threadNames_;
192
0
  }
193
194
0
  const ChromeStackFrameNode &getRoot() const {
195
0
    return *root_;
196
0
  }
197
198
0
  const std::vector<ChromeSampleEvent> &getSampledEvents() const {
199
0
    return sampleEvents_;
200
0
  }
201
};
202
203
/// Serialize input ChromeTraceFormat to output stream.
204
class ChromeTraceSerializer {
205
 private:
206
  const SamplingProfiler &samplingProfiler_;
207
  ChromeTraceFormat trace_;
208
  SamplingProfiler::TimeStampType firstEventTimeStamp_;
209
210
 private:
211
  // Emit process_name metadata event.
212
  void serializeProcessName(JSONEmitter &json) const;
213
  // Emit threads related events.
214
  void serializeThreads(JSONEmitter &json) const;
215
  // Emit "sampled" events for captured stack traces.
216
  void serializeSampledEvents(JSONEmitter &json) const;
217
  // Emit "stackFrames" entries.
218
  void serializeStackFrames(JSONEmitter &json) const;
219
220
  // \return a serializable timeStamp string.
221
  static std::string getSerializedTimeStamp(
222
      SamplingProfiler::TimeStampType timeStamp);
223
224
 public:
225
  explicit ChromeTraceSerializer(
226
      const SamplingProfiler &sp,
227
      ChromeTraceFormat &&chromeTrace);
228
229
  /// Serialize chrome trace to \p OS.
230
  void serialize(llvh::raw_ostream &OS) const;
231
};
232
233
/// Serialize the \p chromeTrace as a Profiler.Profile to \p os. See the url
234
/// below for a description of that type.
235
///
236
/// https://chromedevtools.github.io/devtools-protocol/tot/Profiler/#type-Profile
237
///
238
void serializeAsProfilerProfile(
239
    const SamplingProfiler &sp,
240
    llvh::raw_ostream &os,
241
    ChromeTraceFormat &&chromeTrace);
242
243
} // namespace vm
244
} // namespace hermes
245
246
#endif // HERMESVM_SAMPLING_PROFILER_AVAILABLE
247
248
#endif // HERMES_VM_PROFILER_CHROMETRACESERIALIZER_H