44483152c5
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}
223 lines
6.1 KiB
C++
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
|