summaryrefslogtreecommitdiff
path: root/deps/v8/test/cctest/test-cpu-profiler.cc
diff options
context:
space:
mode:
authorMichaël Zasso <targos@protonmail.com>2020-11-13 12:51:53 +0100
committerMichaël Zasso <targos@protonmail.com>2020-11-15 16:46:54 +0100
commit48db20f6f53060e38b2272566b014741eb4f519f (patch)
treee2f9b4c7f69d2e4597b73b4c3c09f4371d5cc963 /deps/v8/test/cctest/test-cpu-profiler.cc
parent79916428a48df937aa5b2b69c061d2d42181a76b (diff)
downloadnode-new-48db20f6f53060e38b2272566b014741eb4f519f.tar.gz
deps: update V8 to 8.7.220
PR-URL: https://github.com/nodejs/node/pull/35700 Reviewed-By: Rich Trott <rtrott@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Jiawen Geng <technicalcute@gmail.com> Reviewed-By: Shelley Vohr <codebytere@gmail.com>
Diffstat (limited to 'deps/v8/test/cctest/test-cpu-profiler.cc')
-rw-r--r--deps/v8/test/cctest/test-cpu-profiler.cc327
1 files changed, 179 insertions, 148 deletions
diff --git a/deps/v8/test/cctest/test-cpu-profiler.cc b/deps/v8/test/cctest/test-cpu-profiler.cc
index 5d85310179..84c4ee08fe 100644
--- a/deps/v8/test/cctest/test-cpu-profiler.cc
+++ b/deps/v8/test/cctest/test-cpu-profiler.cc
@@ -450,8 +450,7 @@ class ProfilerHelper {
v8::Local<v8::Function> function, v8::Local<v8::Value> argv[], int argc,
unsigned min_js_samples = 0, unsigned min_external_samples = 0,
ProfilingMode mode = ProfilingMode::kLeafNodeLineNumbers,
- unsigned max_samples = v8::CpuProfilingOptions::kNoSampleLimit,
- v8::Local<v8::Context> context = v8::Local<v8::Context>());
+ unsigned max_samples = v8::CpuProfilingOptions::kNoSampleLimit);
v8::CpuProfiler* profiler() { return profiler_; }
@@ -464,12 +463,11 @@ v8::CpuProfile* ProfilerHelper::Run(v8::Local<v8::Function> function,
v8::Local<v8::Value> argv[], int argc,
unsigned min_js_samples,
unsigned min_external_samples,
- ProfilingMode mode, unsigned max_samples,
- v8::Local<v8::Context> context) {
+ ProfilingMode mode, unsigned max_samples) {
v8::Local<v8::String> profile_name = v8_str("my_profile");
profiler_->SetSamplingInterval(100);
- profiler_->StartProfiling(profile_name, {mode, max_samples, 0, context});
+ profiler_->StartProfiling(profile_name, {mode, max_samples, 0});
v8::internal::CpuProfiler* iprofiler =
reinterpret_cast<v8::internal::CpuProfiler*>(profiler_);
@@ -996,7 +994,7 @@ TEST(NativeMethodUninitializedIC) {
v8::Local<v8::Signature> signature =
v8::Signature::New(isolate, func_template);
proto_template->Set(
- v8_str("fooMethod"),
+ isolate, "fooMethod",
v8::FunctionTemplate::New(isolate, &TestApiCallbacks::Callback, data,
signature, 0));
@@ -1037,7 +1035,7 @@ TEST(NativeMethodMonomorphicIC) {
v8::Local<v8::Signature> signature =
v8::Signature::New(isolate, func_template);
proto_template->Set(
- v8_str("fooMethod"),
+ isolate, "fooMethod",
v8::FunctionTemplate::New(isolate, &TestApiCallbacks::Callback, data,
signature, 0));
@@ -1245,7 +1243,7 @@ static const char* call_function_test_source =
" } while (Date.now() - start < duration);\n"
"}";
-// Test that if we sampled thread when it was inside FunctionCall buitin then
+// Test that if we sampled thread when it was inside FunctionCall builtin then
// its caller frame will be '(unresolved function)' as we have no reliable way
// to resolve it.
//
@@ -2111,9 +2109,7 @@ TEST(FunctionDetails) {
"script_b", true);
script_b->Run(env).ToLocalChecked();
const v8::CpuProfile* profile = i::ProfilerExtension::last_profile;
- const v8::CpuProfileNode* current = profile->GetTopDownRoot();
- reinterpret_cast<ProfileNode*>(const_cast<v8::CpuProfileNode*>(current))
- ->Print(0);
+ reinterpret_cast<const i::CpuProfile*>(profile)->Print();
// The tree should look like this:
// 0 (root) 0 #1
// 0 "" 19 #2 no reason script_b:1
@@ -2189,9 +2185,7 @@ TEST(FunctionDetailsInlining) {
script_a->Run(env).ToLocalChecked();
const v8::CpuProfile* profile = i::ProfilerExtension::last_profile;
- const v8::CpuProfileNode* current = profile->GetTopDownRoot();
- reinterpret_cast<ProfileNode*>(const_cast<v8::CpuProfileNode*>(current))
- ->Print(0);
+ reinterpret_cast<const i::CpuProfile*>(profile)->Print();
// The tree should look like this:
// 0 (root) 0 #1
// 5 (program) 0 #6
@@ -2218,6 +2212,84 @@ TEST(FunctionDetailsInlining) {
script_b->GetUnboundScript()->GetId(), 1, 14, alpha);
}
+static const char* pre_profiling_osr_script = R"(
+ function whenPass(pass, optDuration) {
+ if (pass == 5) startProfiling();
+ }
+ function hot(optDuration, deoptDuration) {
+ const startTime = Date.now();
+ %PrepareFunctionForOptimization(hot);
+ for (let pass = 0; pass <= optDuration + deoptDuration; pass++) {
+ // Let a few passes go by to ensure we have enough feeback info
+ if (pass == 3) %OptimizeOsr();
+ // Force deoptimization. %DeoptimizeNow and %DeoptimizeFunction don't
+ // doptimize OSRs.
+ if (pass == optDuration) whenPass = () => {};
+ whenPass(pass, optDuration);
+ for (let i = 0; i < 1e5; i++) {
+ for (let j = 0; j < 1000; j++) {
+ x = Math.random() * j;
+ }
+ if ((Date.now() - startTime) > pass) break;
+ }
+ }
+ }
+ function notHot(optDuration, deoptDuration) {
+ hot(optDuration, deoptDuration);
+ stopProfiling()
+ }
+ )";
+
+// Testing profiling of OSR code that was OSR optimized before profiling
+// started. Currently the behavior is not quite right so we're currently
+// testing a deopt event being sent to the sampling thread for a function
+// it knows nothing about. This deopt does mean we start getting samples
+// for hot so we expect some samples, just fewer than for notHot.
+//
+// We should get something like:
+// 0 (root):0 3 0 #1
+// 12 (garbage collector):0 3 0 #5
+// 5 notHot:22 0 4 #2
+// 85 hot:5 0 4 #6
+// 0 whenPass:2 0 4 #3
+// 0 startProfiling:0 2 0 #4
+//
+// But currently get something like:
+// 0 (root):0 3 0 #1
+// 12 (garbage collector):0 3 0 #5
+// 57 notHot:22 0 4 #2
+// 33 hot:5 0 4 #6
+// 0 whenPass:2 0 4 #3
+// 0 startProfiling:0 2 0 #4
+
+TEST(StartProfilingAfterOsr) {
+ i::FLAG_allow_natives_syntax = true;
+ v8::HandleScope scope(CcTest::isolate());
+ v8::Local<v8::Context> env = CcTest::NewContext({PROFILER_EXTENSION_ID});
+ v8::Context::Scope context_scope(env);
+ ProfilerHelper helper(env);
+ CompileRun(pre_profiling_osr_script);
+ v8::Local<v8::Function> function = GetFunction(env, "notHot");
+
+ int32_t profiling_optimized_ms = 80;
+ int32_t profiling_deoptimized_ms = 40;
+ v8::Local<v8::Value> args[] = {
+ v8::Integer::New(env->GetIsolate(), profiling_optimized_ms),
+ v8::Integer::New(env->GetIsolate(), profiling_deoptimized_ms)};
+ function->Call(env, env->Global(), arraysize(args), args).ToLocalChecked();
+ const v8::CpuProfile* profile = i::ProfilerExtension::last_profile;
+ CHECK(profile);
+ reinterpret_cast<const i::CpuProfile*>(profile)->Print();
+
+ const CpuProfileNode* root = profile->GetTopDownRoot();
+ const v8::CpuProfileNode* notHotNode = GetChild(env, root, "notHot");
+ const v8::CpuProfileNode* hotNode = GetChild(env, notHotNode, "hot");
+ USE(hotNode);
+ // If/when OSR sampling is fixed the following CHECK_GT could/should be
+ // uncommented and the node = node line deleted.
+ // CHECK_GT(hotNode->GetHitCount(), notHotNode->GetHitCount());
+}
+
TEST(DontStopOnFinishedProfileDelete) {
v8::HandleScope scope(CcTest::isolate());
v8::Local<v8::Context> env = CcTest::NewContext({PROFILER_EXTENSION_ID});
@@ -3096,6 +3168,99 @@ TEST(MultipleIsolates) {
thread2.Join();
}
+// Varying called function frame sizes increases the chance of something going
+// wrong if sampling an unlocked frame. We also prevent optimization to prevent
+// inlining so each function call has its own frame.
+const char* varying_frame_size_script = R"(
+ %NeverOptimizeFunction(maybeYield);
+ %NeverOptimizeFunction(bar);
+ %NeverOptimizeFunction(foo);
+ function maybeYield(n) {
+ YieldIsolate(Math.random() > yieldLimit);
+ }
+ function bar(a, b, c, d) {
+ maybeYield(Math.random());
+ return a.length + b.length + c.length + d.length;
+ }
+ function foo(timeLimit, yieldProbability) {
+ yieldLimit = 1 - yieldProbability;
+ const startTime = Date.now();
+ for (let i = 0; i < 1e6; i++) {
+ maybeYield(1);
+ bar("Hickory", "Dickory", "Doc", "Mouse");
+ YieldIsolate(Math.random() > 0.999);
+ if ((Date.now() - startTime) > timeLimit) break;
+ }
+ }
+ )";
+
+class UnlockingThread : public v8::base::Thread {
+ public:
+ explicit UnlockingThread(v8::Local<v8::Context> env)
+ : Thread(Options("UnlockingThread")), env_(CcTest::isolate(), env) {}
+
+ void Run() override {
+ v8::Isolate* isolate = CcTest::isolate();
+ v8::Locker locker(isolate);
+ v8::Isolate::Scope isolate_scope(isolate);
+ v8::HandleScope scope(isolate);
+ v8::Local<v8::Context> env = v8::Local<v8::Context>::New(isolate, env_);
+ Profile(env);
+ }
+
+ static void Profile(v8::Local<v8::Context> env) {
+ v8::Isolate* isolate = CcTest::isolate();
+ v8::Context::Scope context_scope(env);
+ v8::CpuProfiler* profiler = v8::CpuProfiler::New(isolate);
+ profiler->SetSamplingInterval(200);
+ v8::Local<v8::String> profile_name = v8_str("1");
+ profiler->StartProfiling(profile_name);
+ int32_t time_limit = 200;
+ double yield_probability = 0.001;
+ v8::Local<v8::Value> args[] = {v8::Integer::New(isolate, time_limit),
+ v8::Number::New(isolate, yield_probability)};
+ v8::Local<v8::Function> function = GetFunction(env, "foo");
+ function->Call(env, env->Global(), arraysize(args), args).ToLocalChecked();
+ profiler->StopProfiling(profile_name);
+ profiler->Dispose();
+ }
+
+ private:
+ v8::Persistent<v8::Context> env_;
+};
+
+// Checking for crashes with multiple thread/single Isolate profiling.
+TEST(MultipleThreadsSingleIsolate) {
+ i::FLAG_allow_natives_syntax = true;
+ v8::Isolate* isolate = CcTest::isolate();
+ v8::Locker locker(isolate);
+ v8::HandleScope scope(isolate);
+ v8::Local<v8::Context> env = CcTest::NewContext({PROFILER_EXTENSION_ID});
+ v8::Context::Scope context_scope(env);
+ CcTest::AddGlobalFunction(
+ env, "YieldIsolate", [](const v8::FunctionCallbackInfo<v8::Value>& info) {
+ v8::Isolate* isolate = info.GetIsolate();
+ if (!info[0]->IsTrue()) return;
+ v8::Unlocker unlocker(isolate);
+ v8::base::OS::Sleep(v8::base::TimeDelta::FromMilliseconds(1));
+ });
+
+ CompileRun(varying_frame_size_script);
+ UnlockingThread thread1(env);
+ UnlockingThread thread2(env);
+
+ CHECK(thread1.Start());
+ CHECK(thread2.Start());
+
+ // For good measure, profile on our own thread
+ UnlockingThread::Profile(env);
+ {
+ v8::Unlocker unlocker(isolate);
+ thread1.Join();
+ thread2.Join();
+ }
+}
+
// Tests that StopProfiling doesn't wait for the next sample tick in order to
// stop, but rather exits early before a given wait threshold.
TEST(FastStopProfiling) {
@@ -3450,140 +3615,6 @@ TEST(Bug9151StaleCodeEntries) {
CHECK(callback);
}
-// Tests that functions from other contexts aren't recorded when filtering for
-// another context.
-TEST(ContextIsolation) {
- i::FLAG_allow_natives_syntax = true;
- LocalContext execution_env;
- i::HandleScope scope(CcTest::i_isolate());
-
- // Install CollectSample callback for more deterministic sampling.
- v8::Local<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New(
- execution_env.local()->GetIsolate(), CallCollectSample);
- v8::Local<v8::Function> func =
- func_template->GetFunction(execution_env.local()).ToLocalChecked();
- func->SetName(v8_str("CallCollectSample"));
- execution_env->Global()
- ->Set(execution_env.local(), v8_str("CallCollectSample"), func)
- .FromJust();
-
- ProfilerHelper helper(execution_env.local());
- CompileRun(R"(
- function optimized() {
- CallCollectSample();
- }
-
- function unoptimized() {
- CallCollectSample();
- }
-
- function start() {
- // Test optimized functions
- %PrepareFunctionForOptimization(optimized);
- optimized();
- optimized();
- %OptimizeFunctionOnNextCall(optimized);
- optimized();
-
- // Test unoptimized functions
- %NeverOptimizeFunction(unoptimized);
- unoptimized();
-
- // Test callback
- CallCollectSample();
- }
- )");
- v8::Local<v8::Function> function =
- GetFunction(execution_env.local(), "start");
-
- v8::CpuProfile* same_context_profile = helper.Run(
- function, nullptr, 0, 0, 0, v8::CpuProfilingMode::kLeafNodeLineNumbers,
- v8::CpuProfilingOptions::kNoSampleLimit, execution_env.local());
- const v8::CpuProfileNode* root = same_context_profile->GetTopDownRoot();
- const v8::CpuProfileNode* start_node = FindChild(root, "start");
- CHECK(start_node);
- const v8::CpuProfileNode* optimized_node = FindChild(start_node, "optimized");
- CHECK(optimized_node);
- const v8::CpuProfileNode* unoptimized_node =
- FindChild(start_node, "unoptimized");
- CHECK(unoptimized_node);
- const v8::CpuProfileNode* callback_node =
- FindChild(start_node, "CallCollectSample");
- CHECK(callback_node);
-
- {
- LocalContext filter_env;
- v8::CpuProfile* diff_context_profile = helper.Run(
- function, nullptr, 0, 0, 0, v8::CpuProfilingMode::kLeafNodeLineNumbers,
- v8::CpuProfilingOptions::kNoSampleLimit, filter_env.local());
- const v8::CpuProfileNode* diff_root =
- diff_context_profile->GetTopDownRoot();
- // Ensure that no children were recorded (including callbacks, builtins).
- CHECK(!FindChild(diff_root, "start"));
- }
-}
-
-// Tests that when a native context that's being filtered is moved, we continue
-// to track its execution.
-TEST(ContextFilterMovedNativeContext) {
- i::FLAG_allow_natives_syntax = true;
- i::FLAG_manual_evacuation_candidates_selection = true;
- LocalContext env;
- i::HandleScope scope(CcTest::i_isolate());
-
- {
- // Install CollectSample callback for more deterministic sampling.
- v8::Local<v8::FunctionTemplate> sample_func_template =
- v8::FunctionTemplate::New(env.local()->GetIsolate(), CallCollectSample);
- v8::Local<v8::Function> sample_func =
- sample_func_template->GetFunction(env.local()).ToLocalChecked();
- sample_func->SetName(v8_str("CallCollectSample"));
- env->Global()
- ->Set(env.local(), v8_str("CallCollectSample"), sample_func)
- .FromJust();
-
- // Install a function that triggers the native context to be moved.
- v8::Local<v8::FunctionTemplate> move_func_template =
- v8::FunctionTemplate::New(
- env.local()->GetIsolate(),
- [](const v8::FunctionCallbackInfo<v8::Value>& info) {
- i::Isolate* isolate =
- reinterpret_cast<i::Isolate*>(info.GetIsolate());
- i::heap::ForceEvacuationCandidate(
- i::Page::FromHeapObject(isolate->raw_native_context()));
- CcTest::CollectAllGarbage();
- });
- v8::Local<v8::Function> move_func =
- move_func_template->GetFunction(env.local()).ToLocalChecked();
- move_func->SetName(v8_str("ForceNativeContextMove"));
- env->Global()
- ->Set(env.local(), v8_str("ForceNativeContextMove"), move_func)
- .FromJust();
-
- ProfilerHelper helper(env.local());
- CompileRun(R"(
- function start() {
- ForceNativeContextMove();
- CallCollectSample();
- }
- )");
- v8::Local<v8::Function> function = GetFunction(env.local(), "start");
-
- v8::CpuProfile* profile = helper.Run(
- function, nullptr, 0, 0, 0, v8::CpuProfilingMode::kLeafNodeLineNumbers,
- v8::CpuProfilingOptions::kNoSampleLimit, env.local());
- const v8::CpuProfileNode* root = profile->GetTopDownRoot();
- const v8::CpuProfileNode* start_node = FindChild(root, "start");
- CHECK(start_node);
-
- // Verify that after moving the native context, CallCollectSample is still
- // recorded.
- const v8::CpuProfileNode* callback_node =
- FindChild(start_node, "CallCollectSample");
- CHECK(callback_node);
- }
-}
-
enum class EntryCountMode { kAll, kOnlyInlined };
// Count the number of unique source positions.