v8/test/cctest/libsampler/test-sampler.cc
Andrew Comminos 44483152c5 [cpu-profiler] Only record SIGPROF-based samples for samplers that request samples
Sets an atomic field on each sampler when it requests a sample, to be
checked when the SIGPROF handler is executed. A counter is not used
since signals may be coalesced.

Prior to this change, all samplers attached to an isolate received
samples when other samplers sent SIGPROF to the VM thread. This change
alters the behaviour of different CpuProfiler instances on the same
isolate to be in line with the Windows / Fuchsia behaviour.

Bug: v8:8835
Change-Id: I0caaa845b596efc9d8b1cd7716c067d9a6359c57
Reviewed-on: https://chromium-review.googlesource.com/c/1468941
Commit-Queue: Peter Marshall <petermarshall@chromium.org>
Reviewed-by: Peter Marshall <petermarshall@chromium.org>
Reviewed-by: Alexei Filippov <alph@chromium.org>
Cr-Commit-Position: refs/heads/master@{#59545}
2019-02-13 09:29:38 +00:00

223 lines
6.1 KiB
C++

// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Tests of sampler functionalities.
#include "src/libsampler/sampler.h"
#include "src/base/platform/platform.h"
#include "src/base/platform/time.h"
#include "test/cctest/cctest.h"
namespace v8 {
namespace sampler {
namespace {
class TestSamplingThread : public base::Thread {
public:
static const int kSamplerThreadStackSize = 64 * 1024;
explicit TestSamplingThread(Sampler* sampler)
: Thread(base::Thread::Options("TestSamplingThread",
kSamplerThreadStackSize)),
sampler_(sampler) {}
// Implement Thread::Run().
void Run() override {
while (sampler_->IsActive()) {
sampler_->DoSample();
base::OS::Sleep(base::TimeDelta::FromMilliseconds(1));
}
}
private:
Sampler* sampler_;
};
class TestSampler : public Sampler {
public:
explicit TestSampler(Isolate* isolate) : Sampler(isolate) {}
void SampleStack(const v8::RegisterState& regs) override {
void* frames[kMaxFramesCount];
SampleInfo sample_info;
isolate()->GetStackSample(regs, frames, kMaxFramesCount, &sample_info);
if (is_counting_samples_) {
if (sample_info.vm_state == JS) ++js_sample_count_;
if (sample_info.vm_state == EXTERNAL) ++external_sample_count_;
}
}
};
class TestApiCallbacks {
public:
TestApiCallbacks() = default;
static void Getter(v8::Local<v8::String> name,
const v8::PropertyCallbackInfo<v8::Value>& info) {
}
static void Setter(v8::Local<v8::String> name,
v8::Local<v8::Value> value,
const v8::PropertyCallbackInfo<void>& info) {
}
};
static void RunSampler(v8::Local<v8::Context> env,
v8::Local<v8::Function> function,
v8::Local<v8::Value> argv[], int argc,
unsigned min_js_samples = 0,
unsigned min_external_samples = 0) {
TestSampler sampler(env->GetIsolate());
TestSamplingThread thread(&sampler);
sampler.Start();
sampler.StartCountingSamples();
thread.StartSynchronously();
do {
function->Call(env, env->Global(), argc, argv).ToLocalChecked();
} while (sampler.js_sample_count() < min_js_samples ||
sampler.external_sample_count() < min_external_samples);
sampler.Stop();
thread.Join();
}
} // namespace
static const char* sampler_test_source = "function start(count) {\n"
" for (var i = 0; i < count; i++) {\n"
" var o = instance.foo;\n"
" instance.foo = o + 1;\n"
" }\n"
"}\n";
static v8::Local<v8::Function> GetFunction(v8::Local<v8::Context> env,
const char* name) {
return v8::Local<v8::Function>::Cast(
env->Global()->Get(env, v8_str(name)).ToLocalChecked());
}
TEST(LibSamplerCollectSample) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
v8::Local<v8::FunctionTemplate> func_template =
v8::FunctionTemplate::New(isolate);
v8::Local<v8::ObjectTemplate> instance_template =
func_template->InstanceTemplate();
TestApiCallbacks accessors;
v8::Local<v8::External> data =
v8::External::New(isolate, &accessors);
instance_template->SetAccessor(v8_str("foo"), &TestApiCallbacks::Getter,
&TestApiCallbacks::Setter, data);
v8::Local<v8::Function> func =
func_template->GetFunction(env.local()).ToLocalChecked();
v8::Local<v8::Object> instance =
func->NewInstance(env.local()).ToLocalChecked();
env->Global()->Set(env.local(), v8_str("instance"), instance).FromJust();
CompileRun(sampler_test_source);
v8::Local<v8::Function> function = GetFunction(env.local(), "start");
int32_t repeat_count = 100;
v8::Local<v8::Value> args[] = {v8::Integer::New(isolate, repeat_count)};
RunSampler(env.local(), function, args, arraysize(args), 100, 100);
}
#ifdef USE_SIGNALS
class CountingSampler : public Sampler {
public:
explicit CountingSampler(Isolate* isolate) : Sampler(isolate) {}
void SampleStack(const v8::RegisterState& regs) override { sample_count_++; }
int sample_count() { return sample_count_; }
void set_active(bool active) { SetActive(active); }
void set_should_record_sample() { SetShouldRecordSample(); }
private:
int sample_count_ = 0;
};
TEST(SamplerManager_AddRemoveSampler) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
SamplerManager* manager = SamplerManager::instance();
CountingSampler sampler1(isolate);
sampler1.set_active(true);
sampler1.set_should_record_sample();
CHECK_EQ(0, sampler1.sample_count());
manager->AddSampler(&sampler1);
RegisterState state;
manager->DoSample(state);
CHECK_EQ(1, sampler1.sample_count());
sampler1.set_active(true);
sampler1.set_should_record_sample();
manager->RemoveSampler(&sampler1);
sampler1.set_active(false);
manager->DoSample(state);
CHECK_EQ(1, sampler1.sample_count());
}
TEST(SamplerManager_DoesNotReAdd) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
// Add the same sampler twice, but check we only get one sample for it.
SamplerManager* manager = SamplerManager::instance();
CountingSampler sampler1(isolate);
sampler1.set_active(true);
sampler1.set_should_record_sample();
manager->AddSampler(&sampler1);
manager->AddSampler(&sampler1);
RegisterState state;
manager->DoSample(state);
CHECK_EQ(1, sampler1.sample_count());
sampler1.set_active(false);
}
TEST(AtomicGuard_GetNonBlockingSuccess) {
std::atomic_bool atomic{false};
{
AtomicGuard guard(&atomic, false);
CHECK(guard.is_success());
AtomicGuard guard2(&atomic, false);
CHECK(!guard2.is_success());
}
AtomicGuard guard(&atomic, false);
CHECK(guard.is_success());
}
TEST(AtomicGuard_GetBlockingSuccess) {
std::atomic_bool atomic{false};
{
AtomicGuard guard(&atomic);
CHECK(guard.is_success());
AtomicGuard guard2(&atomic, false);
CHECK(!guard2.is_success());
}
AtomicGuard guard(&atomic);
CHECK(guard.is_success());
}
#endif // USE_SIGNALS
} // namespace sampler
} // namespace v8