Commit 89ed6b76 authored by Andrew Comminos's avatar Andrew Comminos Committed by Commit Bot

[cpu-profiler] Add parameter to limit profiler samples taken

To prevent OOMs for leaked CPU profilers, add the option to limit the
maximum number of samples that are included in a CPU profile.

Bug: chromium:956688
Change-Id: I119d0622e7d39c187f8e09e2d49dec91fd724ecb
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1588412
Commit-Queue: Andrew Comminos <acomminos@fb.com>
Reviewed-by: 's avatarPeter Marshall <petermarshall@chromium.org>
Reviewed-by: 's avatarAlexei Filippov <alph@chromium.org>
Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#61352}
parent 7275c9c8
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#ifndef V8_V8_PROFILER_H_ #ifndef V8_V8_PROFILER_H_
#define V8_V8_PROFILER_H_ #define V8_V8_PROFILER_H_
#include <limits.h>
#include <unordered_set> #include <unordered_set>
#include <vector> #include <vector>
#include "v8.h" // NOLINT(build/include) #include "v8.h" // NOLINT(build/include)
...@@ -312,6 +313,9 @@ enum CpuProfilingNamingMode { ...@@ -312,6 +313,9 @@ enum CpuProfilingNamingMode {
*/ */
class V8_EXPORT CpuProfiler { class V8_EXPORT CpuProfiler {
public: public:
// Indicates that the sample buffer size should not be explicitly limited.
static const unsigned kNoSampleLimit = UINT_MAX;
/** /**
* Creates a new CPU profiler for the |isolate|. The isolate must be * Creates a new CPU profiler for the |isolate|. The isolate must be
* initialized. The profiler object must be disposed after use by calling * initialized. The profiler object must be disposed after use by calling
...@@ -358,9 +362,14 @@ class V8_EXPORT CpuProfiler { ...@@ -358,9 +362,14 @@ class V8_EXPORT CpuProfiler {
* *
* |record_samples| parameter controls whether individual samples should * |record_samples| parameter controls whether individual samples should
* be recorded in addition to the aggregated tree. * be recorded in addition to the aggregated tree.
*
* |max_samples| controls the maximum number of samples that should be
* recorded by the profiler. Samples obtained after this limit will be
* discarded.
*/ */
void StartProfiling(Local<String> title, CpuProfilingMode mode, void StartProfiling(Local<String> title, CpuProfilingMode mode,
bool record_samples = false); bool record_samples = false,
unsigned max_samples = kNoSampleLimit);
/** /**
* The same as StartProfiling above, but the CpuProfilingMode defaults to * The same as StartProfiling above, but the CpuProfilingMode defaults to
* kLeafNodeLineNumbers mode, which was the previous default behavior of the * kLeafNodeLineNumbers mode, which was the previous default behavior of the
......
...@@ -10135,13 +10135,14 @@ void CpuProfiler::CollectSample() { ...@@ -10135,13 +10135,14 @@ void CpuProfiler::CollectSample() {
void CpuProfiler::StartProfiling(Local<String> title, bool record_samples) { void CpuProfiler::StartProfiling(Local<String> title, bool record_samples) {
reinterpret_cast<i::CpuProfiler*>(this)->StartProfiling( reinterpret_cast<i::CpuProfiler*>(this)->StartProfiling(
*Utils::OpenHandle(*title), record_samples, kLeafNodeLineNumbers); *Utils::OpenHandle(*title), record_samples, kLeafNodeLineNumbers,
kNoSampleLimit);
} }
void CpuProfiler::StartProfiling(Local<String> title, CpuProfilingMode mode, void CpuProfiler::StartProfiling(Local<String> title, CpuProfilingMode mode,
bool record_samples) { bool record_samples, unsigned max_samples) {
reinterpret_cast<i::CpuProfiler*>(this)->StartProfiling( reinterpret_cast<i::CpuProfiler*>(this)->StartProfiling(
*Utils::OpenHandle(*title), record_samples, mode); *Utils::OpenHandle(*title), record_samples, mode, max_samples);
} }
CpuProfile* CpuProfiler::StopProfiling(Local<String> title) { CpuProfile* CpuProfiler::StopProfiling(Local<String> title) {
......
...@@ -377,16 +377,16 @@ void CpuProfiler::CollectSample() { ...@@ -377,16 +377,16 @@ void CpuProfiler::CollectSample() {
} }
void CpuProfiler::StartProfiling(const char* title, bool record_samples, void CpuProfiler::StartProfiling(const char* title, bool record_samples,
ProfilingMode mode) { ProfilingMode mode, unsigned max_samples) {
if (profiles_->StartProfiling(title, record_samples, mode)) { if (profiles_->StartProfiling(title, record_samples, mode, max_samples)) {
TRACE_EVENT0("v8", "CpuProfiler::StartProfiling"); TRACE_EVENT0("v8", "CpuProfiler::StartProfiling");
StartProcessorIfNotStarted(); StartProcessorIfNotStarted();
} }
} }
void CpuProfiler::StartProfiling(String title, bool record_samples, void CpuProfiler::StartProfiling(String title, bool record_samples,
ProfilingMode mode) { ProfilingMode mode, unsigned max_samples) {
StartProfiling(profiles_->GetName(title), record_samples, mode); StartProfiling(profiles_->GetName(title), record_samples, mode, max_samples);
isolate_->debug()->feature_tracker()->Track(DebugFeatureTracker::kProfiler); isolate_->debug()->feature_tracker()->Track(DebugFeatureTracker::kProfiler);
} }
......
...@@ -255,8 +255,10 @@ class V8_EXPORT_PRIVATE CpuProfiler { ...@@ -255,8 +255,10 @@ class V8_EXPORT_PRIVATE CpuProfiler {
void set_use_precise_sampling(bool); void set_use_precise_sampling(bool);
void CollectSample(); void CollectSample();
void StartProfiling(const char* title, bool record_samples = false, void StartProfiling(const char* title, bool record_samples = false,
ProfilingMode mode = ProfilingMode::kLeafNodeLineNumbers); ProfilingMode mode = ProfilingMode::kLeafNodeLineNumbers,
void StartProfiling(String title, bool record_samples, ProfilingMode mode); unsigned max_samples = v8::CpuProfiler::kNoSampleLimit);
void StartProfiling(String title, bool record_samples, ProfilingMode mode,
unsigned max_samples);
CpuProfile* StopProfiling(const char* title); CpuProfile* StopProfiling(const char* title);
CpuProfile* StopProfiling(String title); CpuProfile* StopProfiling(String title);
int GetProfilesCount(); int GetProfilesCount();
......
...@@ -474,10 +474,12 @@ using v8::tracing::TracedValue; ...@@ -474,10 +474,12 @@ using v8::tracing::TracedValue;
std::atomic<uint32_t> CpuProfile::last_id_; std::atomic<uint32_t> CpuProfile::last_id_;
CpuProfile::CpuProfile(CpuProfiler* profiler, const char* title, CpuProfile::CpuProfile(CpuProfiler* profiler, const char* title,
bool record_samples, ProfilingMode mode) bool record_samples, ProfilingMode mode,
unsigned max_samples)
: title_(title), : title_(title),
record_samples_(record_samples), record_samples_(record_samples),
mode_(mode), mode_(mode),
max_samples_(max_samples),
start_time_(base::TimeTicks::HighResolutionNow()), start_time_(base::TimeTicks::HighResolutionNow()),
top_down_(profiler->isolate()), top_down_(profiler->isolate()),
profiler_(profiler), profiler_(profiler),
...@@ -496,9 +498,13 @@ void CpuProfile::AddPath(base::TimeTicks timestamp, ...@@ -496,9 +498,13 @@ void CpuProfile::AddPath(base::TimeTicks timestamp,
ProfileNode* top_frame_node = ProfileNode* top_frame_node =
top_down_.AddPathFromEnd(path, src_line, update_stats, mode_); top_down_.AddPathFromEnd(path, src_line, update_stats, mode_);
if (record_samples_ && !timestamp.IsNull()) { bool should_record_sample =
record_samples_ && !timestamp.IsNull() &&
(max_samples_ == v8::CpuProfiler::kNoSampleLimit ||
samples_.size() < max_samples_);
if (should_record_sample)
samples_.push_back({top_frame_node, timestamp, src_line}); samples_.push_back({top_frame_node, timestamp, src_line});
}
const int kSamplesFlushCount = 100; const int kSamplesFlushCount = 100;
const int kNodesFlushCount = 10; const int kNodesFlushCount = 10;
...@@ -698,7 +704,8 @@ CpuProfilesCollection::CpuProfilesCollection(Isolate* isolate) ...@@ -698,7 +704,8 @@ CpuProfilesCollection::CpuProfilesCollection(Isolate* isolate)
bool CpuProfilesCollection::StartProfiling(const char* title, bool CpuProfilesCollection::StartProfiling(const char* title,
bool record_samples, bool record_samples,
ProfilingMode mode) { ProfilingMode mode,
unsigned max_samples) {
current_profiles_semaphore_.Wait(); current_profiles_semaphore_.Wait();
if (static_cast<int>(current_profiles_.size()) >= kMaxSimultaneousProfiles) { if (static_cast<int>(current_profiles_.size()) >= kMaxSimultaneousProfiles) {
current_profiles_semaphore_.Signal(); current_profiles_semaphore_.Signal();
...@@ -713,12 +720,11 @@ bool CpuProfilesCollection::StartProfiling(const char* title, ...@@ -713,12 +720,11 @@ bool CpuProfilesCollection::StartProfiling(const char* title,
} }
} }
current_profiles_.emplace_back( current_profiles_.emplace_back(
new CpuProfile(profiler_, title, record_samples, mode)); new CpuProfile(profiler_, title, record_samples, mode, max_samples));
current_profiles_semaphore_.Signal(); current_profiles_semaphore_.Signal();
return true; return true;
} }
CpuProfile* CpuProfilesCollection::StopProfiling(const char* title) { CpuProfile* CpuProfilesCollection::StopProfiling(const char* title) {
const bool empty_title = (title[0] == '\0'); const bool empty_title = (title[0] == '\0');
CpuProfile* profile = nullptr; CpuProfile* profile = nullptr;
......
...@@ -367,7 +367,7 @@ class CpuProfile { ...@@ -367,7 +367,7 @@ class CpuProfile {
}; };
CpuProfile(CpuProfiler* profiler, const char* title, bool record_samples, CpuProfile(CpuProfiler* profiler, const char* title, bool record_samples,
ProfilingMode mode); ProfilingMode mode, unsigned max_samples);
// Add pc -> ... -> main() call path to the profile. // Add pc -> ... -> main() call path to the profile.
void AddPath(base::TimeTicks timestamp, const ProfileStackTrace& path, void AddPath(base::TimeTicks timestamp, const ProfileStackTrace& path,
...@@ -394,6 +394,7 @@ class CpuProfile { ...@@ -394,6 +394,7 @@ class CpuProfile {
const char* title_; const char* title_;
bool record_samples_; bool record_samples_;
ProfilingMode mode_; ProfilingMode mode_;
const unsigned max_samples_;
base::TimeTicks start_time_; base::TimeTicks start_time_;
base::TimeTicks end_time_; base::TimeTicks end_time_;
std::deque<SampleInfo> samples_; std::deque<SampleInfo> samples_;
...@@ -451,7 +452,8 @@ class V8_EXPORT_PRIVATE CpuProfilesCollection { ...@@ -451,7 +452,8 @@ class V8_EXPORT_PRIVATE CpuProfilesCollection {
void set_cpu_profiler(CpuProfiler* profiler) { profiler_ = profiler; } void set_cpu_profiler(CpuProfiler* profiler) { profiler_ = profiler; }
bool StartProfiling(const char* title, bool record_samples, bool StartProfiling(const char* title, bool record_samples,
ProfilingMode mode = ProfilingMode::kLeafNodeLineNumbers); ProfilingMode mode = ProfilingMode::kLeafNodeLineNumbers,
unsigned max_samples = v8::CpuProfiler::kNoSampleLimit);
CpuProfile* StopProfiling(const char* title); CpuProfile* StopProfiling(const char* title);
std::vector<std::unique_ptr<CpuProfile>>* profiles() { std::vector<std::unique_ptr<CpuProfile>>* profiles() {
return &finished_profiles_; return &finished_profiles_;
......
...@@ -441,7 +441,8 @@ class ProfilerHelper { ...@@ -441,7 +441,8 @@ class ProfilerHelper {
unsigned min_js_samples = 0, unsigned min_js_samples = 0,
unsigned min_external_samples = 0, unsigned min_external_samples = 0,
bool collect_samples = false, bool collect_samples = false,
ProfilingMode mode = ProfilingMode::kLeafNodeLineNumbers); ProfilingMode mode = ProfilingMode::kLeafNodeLineNumbers,
unsigned max_samples = v8::CpuProfiler::kNoSampleLimit);
v8::CpuProfiler* profiler() { return profiler_; } v8::CpuProfiler* profiler() { return profiler_; }
...@@ -454,11 +455,12 @@ v8::CpuProfile* ProfilerHelper::Run(v8::Local<v8::Function> function, ...@@ -454,11 +455,12 @@ v8::CpuProfile* ProfilerHelper::Run(v8::Local<v8::Function> function,
v8::Local<v8::Value> argv[], int argc, v8::Local<v8::Value> argv[], int argc,
unsigned min_js_samples, unsigned min_js_samples,
unsigned min_external_samples, unsigned min_external_samples,
bool collect_samples, ProfilingMode mode) { bool collect_samples, ProfilingMode mode,
unsigned max_samples) {
v8::Local<v8::String> profile_name = v8_str("my_profile"); v8::Local<v8::String> profile_name = v8_str("my_profile");
profiler_->SetSamplingInterval(100); profiler_->SetSamplingInterval(100);
profiler_->StartProfiling(profile_name, mode, collect_samples); profiler_->StartProfiling(profile_name, mode, collect_samples, max_samples);
v8::internal::CpuProfiler* iprofiler = v8::internal::CpuProfiler* iprofiler =
reinterpret_cast<v8::internal::CpuProfiler*>(profiler_); reinterpret_cast<v8::internal::CpuProfiler*>(profiler_);
...@@ -2957,6 +2959,31 @@ TEST(DebugNaming) { ...@@ -2957,6 +2959,31 @@ TEST(DebugNaming) {
profiler->Dispose(); profiler->Dispose();
} }
TEST(SampleLimit) {
LocalContext env;
i::Isolate* isolate = CcTest::i_isolate();
i::HandleScope scope(isolate);
CompileRun(R"(
function start() {
let val = 1;
for (let i = 0; i < 10e3; i++) {
val = (val * 2) % 3;
}
return val;
}
)");
// Take 100 samples of `start`, but set the max samples to 50.
v8::Local<v8::Function> function = GetFunction(env.local(), "start");
ProfilerHelper helper(env.local());
v8::CpuProfile* profile =
helper.Run(function, nullptr, 0, 100, 0, true,
v8::CpuProfilingMode::kLeafNodeLineNumbers, 50);
CHECK_EQ(profile->GetSamplesCount(), 50);
}
enum class EntryCountMode { kAll, kOnlyInlined }; enum class EntryCountMode { kAll, kOnlyInlined };
// Count the number of unique source positions. // Count the number of unique source positions.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment