diff options
author | Michaël Zasso <targos@protonmail.com> | 2022-09-21 13:28:42 +0200 |
---|---|---|
committer | Michaël Zasso <targos@protonmail.com> | 2022-10-11 07:24:33 +0200 |
commit | 6bd756d7c6dfb7dc25daee329ad70df68c14223e (patch) | |
tree | af93818c545f5bd04cafd4a0c19817e19a475641 /deps/v8/test/unittests/libsampler | |
parent | 624dadb00706a9fc08f919ac72941cdaba7e3ec9 (diff) | |
download | node-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.cc | 217 | ||||
-rw-r--r-- | deps/v8/test/unittests/libsampler/signals-and-mutexes-unittest.cc | 165 |
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 |