summaryrefslogtreecommitdiff
path: root/deps/v8/test/unittests/libsampler
diff options
context:
space:
mode:
authorMichaël Zasso <targos@protonmail.com>2022-09-21 13:28:42 +0200
committerMichaël Zasso <targos@protonmail.com>2022-10-11 07:24:33 +0200
commit6bd756d7c6dfb7dc25daee329ad70df68c14223e (patch)
treeaf93818c545f5bd04cafd4a0c19817e19a475641 /deps/v8/test/unittests/libsampler
parent624dadb00706a9fc08f919ac72941cdaba7e3ec9 (diff)
downloadnode-new-6bd756d7c6dfb7dc25daee329ad70df68c14223e.tar.gz
deps: update V8 to 10.7.193.13
PR-URL: https://github.com/nodejs/node/pull/44741 Fixes: https://github.com/nodejs/node/issues/44650 Fixes: https://github.com/nodejs/node/issues/37472 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Jiawen Geng <technicalcute@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
Diffstat (limited to 'deps/v8/test/unittests/libsampler')
-rw-r--r--deps/v8/test/unittests/libsampler/sampler-unittest.cc217
-rw-r--r--deps/v8/test/unittests/libsampler/signals-and-mutexes-unittest.cc165
2 files changed, 382 insertions, 0 deletions
diff --git a/deps/v8/test/unittests/libsampler/sampler-unittest.cc b/deps/v8/test/unittests/libsampler/sampler-unittest.cc
new file mode 100644
index 0000000000..2eb2799ade
--- /dev/null
+++ b/deps/v8/test/unittests/libsampler/sampler-unittest.cc
@@ -0,0 +1,217 @@
+// 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 "include/v8-external.h"
+#include "include/v8-function.h"
+#include "src/base/platform/platform.h"
+#include "src/base/platform/time.h"
+#include "test/unittests/test-utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace v8 {
+
+using SamplerTest = TestWithContext;
+
+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 env->Global()
+ ->Get(env, String::NewFromUtf8(env->GetIsolate(), name).ToLocalChecked())
+ .ToLocalChecked()
+ .As<v8::Function>();
+}
+
+TEST_F(SamplerTest, LibSamplerCollectSample) {
+ 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(NewString("foo"), &TestApiCallbacks::Getter,
+ &TestApiCallbacks::Setter, data);
+ v8::Local<v8::Function> func =
+ func_template->GetFunction(context()).ToLocalChecked();
+ v8::Local<v8::Object> instance =
+ func->NewInstance(context()).ToLocalChecked();
+ context()
+ ->Global()
+ ->Set(context(), NewString("instance"), instance)
+ .FromJust();
+
+ RunJS(sampler_test_source);
+ v8::Local<v8::Function> function = GetFunction(context(), "start");
+
+ int32_t repeat_count = 100;
+ v8::Local<v8::Value> args[] = {v8::Integer::New(isolate(), repeat_count)};
+ RunSampler(context(), 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_F(SamplerTest, SamplerManager_AddRemoveSampler) {
+ 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_F(SamplerTest, SamplerManager_DoesNotReAdd) {
+ // 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_F(SamplerTest, 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_F(SamplerTest, 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
diff --git a/deps/v8/test/unittests/libsampler/signals-and-mutexes-unittest.cc b/deps/v8/test/unittests/libsampler/signals-and-mutexes-unittest.cc
new file mode 100644
index 0000000000..a72b47660d
--- /dev/null
+++ b/deps/v8/test/unittests/libsampler/signals-and-mutexes-unittest.cc
@@ -0,0 +1,165 @@
+// Copyright 2021 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.
+
+#include <signal.h>
+
+#include "src/base/platform/mutex.h"
+#include "src/base/platform/platform.h"
+#include "src/base/platform/time.h"
+#include "src/base/utils/random-number-generator.h"
+#include "src/libsampler/sampler.h" // for USE_SIGNALS
+#include "test/unittests/test-utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace v8 {
+
+using SignalAndMutexTest = TestWithContext;
+namespace sampler {
+
+// There seem to be problems with pthread_rwlock_t and signal handling on
+// Mac, see https://crbug.com/v8/11399 and
+// https://stackoverflow.com/questions/22643374/deadlock-with-pthread-rwlock-t-and-signals
+// This test reproduces it, and can be used to test if this problem is fixed in
+// future Mac releases.
+// Note: For now, we fall back to using pthread_mutex_t to implement SharedMutex
+// on Mac, so this test succeeds.
+
+#ifdef USE_SIGNALS
+
+void HandleProfilerSignal(int signal, siginfo_t*, void*) {
+ CHECK_EQ(SIGPROF, signal);
+}
+
+struct sigaction old_signal_handler;
+
+void InstallSignalHandler() {
+ struct sigaction sa;
+ sa.sa_sigaction = &HandleProfilerSignal;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
+ CHECK_EQ(0, sigaction(SIGPROF, &sa, &old_signal_handler));
+}
+
+static void RestoreSignalHandler() {
+ sigaction(SIGPROF, &old_signal_handler, nullptr);
+}
+
+TEST_F(SignalAndMutexTest, SignalsPlusSharedMutexes) {
+ static constexpr int kNumMutexes = 1024;
+ // 10us * 10000 = 100ms
+ static constexpr auto kSleepBetweenSamples =
+ base::TimeDelta::FromMicroseconds(10);
+ static constexpr size_t kNumSamples = 10000;
+
+ // Keep a set of all currently running threads. This is filled by the threads
+ // themselves, because we need to get their pthread id.
+ base::Mutex threads_mutex;
+ std::set<pthread_t> threads_to_sample;
+ auto SendSignalToThreads = [&threads_mutex, &threads_to_sample] {
+ base::MutexGuard guard(&threads_mutex);
+ for (pthread_t tid : threads_to_sample) {
+ pthread_kill(tid, SIGPROF);
+ }
+ };
+ auto AddThreadToSample = [&threads_mutex, &threads_to_sample](pthread_t tid) {
+ base::MutexGuard guard(&threads_mutex);
+ CHECK(threads_to_sample.insert(tid).second);
+ };
+ auto RemoveThreadToSample = [&threads_mutex,
+ &threads_to_sample](pthread_t tid) {
+ base::MutexGuard guard(&threads_mutex);
+ CHECK_EQ(1, threads_to_sample.erase(tid));
+ };
+
+ // The sampling threads periodically sends a SIGPROF to all running threads.
+ class SamplingThread : public base::Thread {
+ public:
+ explicit SamplingThread(std::atomic<size_t>* remaining_samples,
+ std::function<void()> send_signal_to_threads)
+ : Thread(base::Thread::Options{"SamplingThread"}),
+ remaining_samples_(remaining_samples),
+ send_signal_to_threads_(std::move(send_signal_to_threads)) {}
+
+ void Run() override {
+ DCHECK_LT(0, remaining_samples_->load(std::memory_order_relaxed));
+ while (remaining_samples_->fetch_sub(1, std::memory_order_relaxed) > 1) {
+ send_signal_to_threads_();
+ base::OS::Sleep(kSleepBetweenSamples);
+ }
+ DCHECK_EQ(0, remaining_samples_->load(std::memory_order_relaxed));
+ }
+
+ private:
+ std::atomic<size_t>* const remaining_samples_;
+ const std::function<void()> send_signal_to_threads_;
+ };
+
+ // These threads repeatedly lock and unlock a shared mutex, both in shared and
+ // exclusive mode. This should not deadlock.
+ class SharedMutexTestThread : public base::Thread {
+ public:
+ SharedMutexTestThread(
+ base::SharedMutex* mutexes, std::atomic<size_t>* remaining_samples,
+ int64_t rng_seed, std::function<void(pthread_t)> add_thread_to_sample,
+ std::function<void(pthread_t)> remove_thread_to_sample)
+ : Thread(Thread::Options{"SharedMutexTestThread"}),
+ mutexes_(mutexes),
+ remaining_samples_(remaining_samples),
+ rng_(rng_seed),
+ add_thread_to_sample_(add_thread_to_sample),
+ remove_thread_to_sample_(remove_thread_to_sample) {}
+
+ void Run() override {
+ add_thread_to_sample_(pthread_self());
+ while (remaining_samples_->load(std::memory_order_relaxed) > 0) {
+ size_t idx = rng_.NextInt(kNumMutexes);
+ base::SharedMutex* mutex = &mutexes_[idx];
+ if (rng_.NextBool()) {
+ base::SharedMutexGuard<base::kShared> guard{mutex};
+ } else {
+ base::SharedMutexGuard<base::kExclusive> guard{mutex};
+ }
+ }
+ remove_thread_to_sample_(pthread_self());
+ }
+
+ private:
+ base::SharedMutex* mutexes_;
+ std::atomic<size_t>* remaining_samples_;
+ base::RandomNumberGenerator rng_;
+ std::function<void(pthread_t)> add_thread_to_sample_;
+ std::function<void(pthread_t)> remove_thread_to_sample_;
+ };
+
+ std::atomic<size_t> remaining_samples{kNumSamples};
+ base::SharedMutex mutexes[kNumMutexes];
+
+ InstallSignalHandler();
+
+ auto* rng = i_isolate()->random_number_generator();
+
+ // First start the mutex threads, then the sampling thread.
+ std::vector<std::unique_ptr<SharedMutexTestThread>> threads(4);
+ for (auto& thread : threads) {
+ thread = std::make_unique<SharedMutexTestThread>(
+ mutexes, &remaining_samples, rng->NextInt64(), AddThreadToSample,
+ RemoveThreadToSample);
+ CHECK(thread->Start());
+ }
+
+ SamplingThread sampling_thread(&remaining_samples, SendSignalToThreads);
+ CHECK(sampling_thread.Start());
+
+ // Wait for the sampling thread to be done. The mutex threads should finish
+ // shortly after.
+ sampling_thread.Join();
+ for (auto& thread : threads) thread->Join();
+
+ RestoreSignalHandler();
+}
+
+#endif // USE_SIGNALS
+
+} // namespace sampler
+} // namespace v8