《V8源码分析》
作者:互联网
https://blog.csdn.net/counsellor/category_9549440.html
(4 条消息)V8 源码分析之 d8 源码注解 (第五篇)_counsellor 的专栏 - CSDN 博客_v8 源码分析之 d8
0x00 前言
没了你,我颓废了自己。心里那些苦,都只哽在喉咙里,一想起来就泪如雨下。 ---- 王国维
0x01 调用栈
Thread 1 "d8" received signal SIGINT, Interrupt. 0x00007ffff4a8ea44 in v8::base::LocalKeyToPthreadKey (local_key=32767) at ../../src/base/platform/platform-posix.cc:856 856 static pthread_key_t LocalKeyToPthreadKey(Thread::LocalStorageKey local_key) { (gdb) bt #0 0x00007ffff4a8ea44 in v8::base::LocalKeyToPthreadKey (local_key=32767) at ../../src/base/platform/platform-posix.cc:856 #1 0x00007ffff4a8ea6a in v8::base::Thread::GetThreadLocal (key=3) at ../../src/base/platform/platform-posix.cc:952 #2 0x00007ffff6929720 in v8::internal::PerThreadAssertData::GetCurrent () at ../../src/common/assert-scope.cc:45 #3 0x00007ffff692ad2b in v8::internal::PerThreadAssertScope<(v8::internal::PerThreadAssertType)2, true>::IsAllowed () at ../../src/common/assert-scope.cc:91 #4 0x00007ffff6ac3a00 in v8::internal::HandleBase::IsDereferenceAllowed (this=0x7fffffffc360) at ../../src/handles/handles.cc:43 #5 0x000055555561a9c5 in v8::internal::HandleBase::location (this=0x7fffffffc360) at ../../src/handles/handles.h:59 #6 0x00007ffff689e560 in v8::internal::Handle<v8::internal::SeqOneByteString>::operator* (this=0x7fffffffc360) at ../../src/handles/handles.h:137 #7 0x00007ffff689e2b5 in v8::internal::Handle<v8::internal::SeqOneByteString>::operator-> (this=0x7fffffffc360) at ../../src/handles/handles.h:131 #8 0x00007ffff6aee3a3 in v8::internal::Factory::NewRawOneByteString (this=0x14e800000000, length=267, allocation=v8::internal::AllocationType::kYoung) at ../../src/heap/factory.cc:1085 #9 0x00007ffff6aee5cb in v8::internal::Factory::NewStringFromUtf8 (this=0x14e800000000, string=..., allocation=v8::internal::AllocationType::kYoung) at ../../src/heap/factory.cc:787 #10 0x00007ffff66d0f43 in v8::(anonymous namespace)::NewString (factory=0x14e800000000, type=v8::NewStringType::kNormal, string=...) at ../../src/api/api.cc:6248 #11 0x00007ffff66d0d97 in v8::String::NewFromUtf8 (isolate=0x14e800000000, data=0x1426612c1000 "127.0.0.1\tlocalhost\n127.0.1.1\tubuntu\n127.0.0.1 chrome-infra-packages.appspot.com\n\n# The following lines are desirable for IPv6 capable hosts\n::1 ip6-localhost ip6-loopback\nfe00::0 ip6-localnet\nf"..., type=v8::NewStringType::kNormal, length=267) at ../../src/api/api.cc:6297 #12 0x00005555555f178e in v8::Shell::ReadFile (isolate=0x14e800000000, name=0x55555570aa30 "/etc/hosts") at ../../src/d8/d8.cc:2358 #13 0x00005555555f83e3 in v8::Shell::Read (args=...) at ../../src/d8/d8.cc:1412 #14 0x00007ffff6081f0f in Builtins_CallApiCallback () from /home/test/git/google/v8/out/x64.debug/libv8.so #15 0x00007fffffffc768 in ?? () #16 0x00007fffffffc798 in ?? () #17 0x0000000000000001 in ?? () #18 0x0000000000000040 in ?? () #19 0x00007fffffffc7a0 in ?? () #20 0x00007fffffffc720 in ?? () #21 0x0000000000000006 in ?? () #22 0x00007fffffffc7f0 in ?? () #23 0x000014e800082c3e in ?? () #24 0x000014e8083c0119 in ?? () #25 0x000014e800000000 in ?? () #26 0x000014e808040305 in ?? () #27 0x000014e808040305 in ?? () #28 0x000014e808040305 in ?? () #29 0x000014e808040305 in ?? () #30 0x000014e80824fdbd in ?? () #31 0x000014e8083c0119 in ?? () #32 0x000014e808240ced in ?? () #33 0x000014e80824fec1 in ?? () #34 0x000014e80824fdbd in ?? () #35 0x000014e80824e125 in ?? () #36 0x000014e80836a2d9 in ?? () #37 0x0000000000000098 in ?? () #38 0x000014e80824fe59 in ?? () #39 0x000014e80824fec1 in ?? () #40 0x000014e808240ced in ?? () #41 0x00007fffffffc818 in ?? () #42 0x00007ffff60753da in Builtins_JSEntryTrampoline () from /home/test/git/google/v8/out/x64.debug/libv8.so #43 0x000014e8083c0119 in ?? () #44 0x000014e80824fec1 in ?? () #45 0x0000000000000022 in ?? () #46 0x00007fffffffc880 in ?? () #47 0x00007ffff60751b8 in Builtins_JSEntry () from /home/test/git/google/v8/out/x64.debug/libv8.so
0x02 RunMain 函数分析
路径
v8\src\d8\d8.cc
源码
int Shell::RunMain(Isolate* isolate, int argc, char* argv[], bool last_run) { //默认d8启动一个线程跑,options.num_isolates默认值为1 for (int i = 1; i < options.num_isolates; ++i) { options.isolate_sources[i].StartExecuteInThread(); } bool success = true; { SetWaitUntilDone(isolate, false); // 默认为null,不会走这个分支 if (options.lcov_file) { debug::Coverage::SelectMode(isolate, debug::CoverageMode::kBlockCount); } //创建HandleScope类的实例对象scope //HandleScope类基于栈分配方式管理所有local句柄,相当于local句柄的作用域。 //所有的本地句柄都由该作用域分配。 //定义位于../../src/api/api.cc:1124 HandleScope scope(isolate); //可以看见0x02小结的注解 Local<Context> context = CreateEvaluationContext(isolate); //last_run 此时为true,use_interactive_shell()用于判断是否为交互shell bool use_existing_context = last_run && use_interactive_shell(); //LZ运行的js文件,所以这里为false,跳过 if (use_existing_context) { // Keep using the same context in the interactive shell. evaluation_context_.Reset(isolate, context); } { //创建Scope类型的作用域,用于管理context Context::Scope cscope(context); //创建InspectorClient,默认options.enable_inspector为false InspectorClient inspector_client(context, options.enable_inspector); //自定义了一个scope PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate)); //开始运行第一个isolate孤岛,暂定这样翻译吧 //LZ放了一个死循环,gdb的是否就会停在这里,等待中断 if (!options.isolate_sources[0].Execute(isolate)) success = false; if (!CompleteMessageLoop(isolate)) success = false; } if (!use_existing_context) { DisposeModuleEmbedderData(context); } WriteLcovData(isolate, options.lcov_file); } //回收isolate CollectGarbage(isolate); //多线程的情况,需要启动或等待每个线程。不太懂last_run是啥意思 // 可以看到索引从1开始,0代表第一个线程,已经执行过了,从1以后需要单独执行 for (int i = 1; i < options.num_isolates; ++i) { if (last_run) { options.isolate_sources[i].JoinThread(); } else { options.isolate_sources[i].WaitForThread(); } } //等待所有worker结束 WaitForRunningWorkers(); // In order to finish successfully, success must be != expected_to_throw. return success == Shell::options.expected_to_throw ? 1 : 0; }
0x03 CreateEvaluationContext 函数分析
这个函数是 d8.cc 中用于创建一段内置 js 代码的自定义函数。这里创建 shell 中内置的 arguments 参数变量。
Local<Context> Shell::CreateEvaluationContext(Isolate* isolate) { // This needs to be a critical section since this is not thread-safe base::MutexGuard lock_guard(context_mutex_.Pointer()); // Initialize the global objects //创建全局模板 Local<ObjectTemplate> global_template = CreateGlobalTemplate(isolate); //创建一个作用域handle EscapableHandleScope handle_scope(isolate); //创建一个context上下文 Local<Context> context = Context::New(isolate, nullptr, global_template); DCHECK(!context.IsEmpty()); if (i::FLAG_perf_prof_annotate_wasm || i::FLAG_vtune_prof_annotate_wasm) { isolate->SetWasmLoadSourceMapCallback(ReadFile); } //初始化嵌入器数据 //在context中指定的索引index位置,预设EmbedderData,用于保存一些自定义数据 InitializeModuleEmbedderData(context); //d8是否包含参数,就是d8 '--'后面跟随的参数列表 if (options.include_arguments) { //创建Scope作用域,用于管理context对象 Context::Scope scope(context); //保存arguments的值 const std::vector<const char*>& args = options.arguments; //arguments的个数 int size = static_cast<int>(args.size()); //在isolate中创建一个lLocal数组变量 Local<Array> array = Array::New(isolate, size); //遍历arguments的值 for (int i = 0; i < size; i++) { //创建一个string类型的Local变量,urf8编码,arguments的值用于初始化 Local<String> arg = v8::String::NewFromUtf8(isolate, args[i], v8::NewStringType::kNormal) .ToLocalChecked(); // 创建Number类型的Local变量,保存arguments的索引 Local<Number> index = v8::Number::New(isolate, i); //给js语言中数据赋上js语言的index和字符串的值。 array->Set(context, index, arg).FromJust(); } //这个js数组变量还没有名字,此时创建一个Local<String> name,初始值为"arguments",即为我们 //在d8中使用的arguments数组变量 Local<String> name = String::NewFromUtf8(isolate, "arguments", NewStringType::kInternalized) .ToLocalChecked(); //在context上下文中加入名为name的array数据 context->Global()->Set(context, name, array).FromJust(); } //返回带有此上下文的作用域handle_scope句柄 //【q1】擦,为什么escape后就变成一个context了,为什么不直接返回一个context? return handle_scope.Escape(context); }
0x04 参考文献
https://v8docs.nodesource.com/node-0.8/df/d69/classv8_1_1_context.html
https://zhuanlan.zhihu.com/p/35371048
https://www.yuque.com/killa/node/v8-api
全文完
本文由 简悦 SimpRead 优化,用以提升阅读体验 使用了 全新的简悦词法分析引擎 beta,点击查看详细说明0x00 前言0x01 调用栈0x02 RunMain 函数分析路径源码0x03 CreateEvaluationContext 函数分析0x04 参考文献
V8 源码分析之 d8 源码注解 (第六篇)_counsellor 的专栏 - CSDN 博客_kconsumecodecache
0x00 前言
d8 自己封装了一个 js 代码执行器,上一篇我们的代码执行到options.isolate_sources[0].Execute(isolate
,本文将做进一步分析。
0x01 调用栈
#0 v8::SourceGroup::Execute (this=0x5555556542a8, isolate=0x1e9d00000000) at ../../src/d8/d8.cc:2567 #1 0x000055555560008b in v8::Shell::RunMain (isolate=0x1e9d00000000, argc=2, argv=0x7fffffffdf38, last_run=true) at ../../src/d8/d8.cc:3100 #2 0x00005555556013a6 in v8::Shell::Main (argc=2, argv=0x7fffffffdf38) at ../../src/d8/d8.cc:3741 #3 0x00005555556016e2 in main (argc=2, argv=0x7fffffffdf38) at ../../src/d8/d8.cc:3777
0x02 SourceGroup::Execute 函数
bool SourceGroup::Execute(Isolate* isolate) { bool success = true; for (int i = begin_offset_; i < end_offset_; ++i) { const char* arg = argv_[i]; //解析-e参数,d8需要执行一段js代码字符串的话,走这个条件分支 if (strcmp(arg, "-e") == 0 && i + 1 < end_offset_) { // Execute argument given to -e option directly. //创建作用域 HandleScope handle_scope(isolate); //创建匿名文件名 Local<String> file_name = String::NewFromUtf8(isolate, "unnamed", NewStringType::kNormal) .ToLocalChecked(); //读取js代码 Local<String> source = String::NewFromUtf8(isolate, argv_[i + 1], NewStringType::kNormal) .ToLocalChecked(); Shell::set_script_executed(); //执行js代码字符串 if (!Shell::ExecuteString(isolate, source, file_name, Shell::kNoPrintResult, Shell::kReportExceptions, Shell::kNoProcessMessageQueue)) { success = false; break; } ++i; continue; //判断是否为js的module文件,规则后缀名必须为.mjs与--module参数连用 } else if (ends_with(arg, ".mjs")) { Shell::set_script_executed(); if (!Shell::ExecuteModule(isolate, arg)) { success = false; break; } continue; // 判断是否为module执行模式 } else if (strcmp(arg, "--module") == 0 && i + 1 < end_offset_) { // Treat the next file as a module. arg = argv_[++i]; Shell::set_script_executed(); if (!Shell::ExecuteModule(isolate, arg)) { success = false; break; } continue; } else if (arg[0] == '-') { // Ignore other options. They have been parsed already. continue; } //LZ这里的执行命令为d8 test.js,所以前面的逻辑都会跳过,真正的入口位置在这里 // Use all other arguments as names of files to load and run. //定义作用域 HandleScope handle_scope(isolate); //创建文件名字符串 Local<String> file_name = String::NewFromUtf8(isolate, arg, NewStringType::kNormal) .ToLocalChecked(); //从文件中读取文件内容 Local<String> source = ReadFile(isolate, arg); if (source.IsEmpty()) { printf("Error reading '%s'\n", arg); base::OS::ExitProcess(1); } //设置执行状态为true,该静态函数在d8.h中定义 Shell::set_script_executed(); //执行js代码 if (!Shell::ExecuteString(isolate, source, file_name, Shell::kNoPrintResult, Shell::kReportExceptions, Shell::kProcessMessageQueue)) { success = false; break; } } return success; }
0x03 Shell::ExecuteString 函数
// Executes a string within the current v8 context. bool Shell::ExecuteString(Isolate* isolate, Local<String> source, Local<Value> name, PrintResult print_result, ReportExceptions report_exceptions, ProcessMessageQueue process_message_queue) { //i::FLAG_parse_only 为false if (i::FLAG_parse_only) { i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); i::VMState<PARSER> state(i_isolate); i::Handle<i::String> str = Utils::OpenHandle(*(source)); // Set up ParseInfo. i::ParseInfo parse_info(i_isolate); parse_info.set_toplevel(); parse_info.set_allow_lazy_parsing(); parse_info.set_language_mode( i::construct_language_mode(i::FLAG_use_strict)); parse_info.set_script( parse_info.CreateScript(i_isolate, str, options.compile_options)); if (!i::parsing::ParseProgram(&parse_info, i_isolate)) { fprintf(stderr, "Failed parsing\n"); return false; } return true; } HandleScope handle_scope(isolate); TryCatch try_catch(isolate); try_catch.SetVerbose(true); MaybeLocal<Value> maybe_result; bool success = true; { //获取自定义数据,get后为null。 PerIsolateData* data = PerIsolateData::Get(isolate); //创建realm变量 Local<Context> realm = Local<Context>::New(isolate, data->realms_[data->realm_current_]); Context::Scope context_scope(realm); MaybeLocal<Script> maybe_script; //创建当前上下文context Local<Context> context(isolate->GetCurrentContext()); //创建ScriptOrigin对象origin ScriptOrigin origin(name); //v8有code caching功能,输入的js代码第一次被编译后,会生成一份cache,再次运行这份js代码时,会优先加载cache,减少重复编译带来的开销。 //编译选项如果为ScriptCompiler::kConsumeCodeCache,则寻找cache并加载 if (options.compile_options == ScriptCompiler::kConsumeCodeCache) { //根据js代码字符串搜索cache ScriptCompiler::CachedData* cached_code = LookupCodeCache(isolate, source); //如果cache不为空 if (cached_code != nullptr) { ScriptCompiler::Source script_source(source, origin, cached_code); maybe_script = ScriptCompiler::Compile(context, &script_source, options.compile_options); CHECK(!cached_code->rejected); } else { ScriptCompiler::Source script_source(source, origin); maybe_script = ScriptCompiler::Compile( context, &script_source, ScriptCompiler::kNoCompileOptions); } // options.stress_background_compile为true,则后台编译 } else if (options.stress_background_compile) { // 启动一个后台线程用于编译js脚本,后台编译就是script streaming 的优化方式 // 同code cache的地位一样重要。浏览器加载多个js脚本时,边下载边编译的过程称为script streaming // Start a background thread compiling the script. BackgroundCompileThread background_compile_thread(isolate, source); //检查线程是否已经就绪 CHECK(background_compile_thread.Start()); // In parallel, compile on the main thread to flush out any data races. { TryCatch ignore_try_catch(isolate); ScriptCompiler::Source script_source(source, origin); USE(ScriptCompiler::Compile(context, &script_source, ScriptCompiler::kNoCompileOptions)); } // Join with background thread and finalize compilation. background_compile_thread.Join(); maybe_script = v8::ScriptCompiler::Compile( context, background_compile_thread.streamed_source(), source, origin); } else { //没有任何优化的编译方式 ScriptCompiler::Source script_source(source, origin); maybe_script = ScriptCompiler::Compile(context, &script_source, options.compile_options); } //定义script变量 Local<Script> script; if (!maybe_script.ToLocal(&script)) { //打印编译过程中的所有报错 // Print errors that happened during compilation. if (report_exceptions) ReportException(isolate, &try_catch); return false; } //如果kProduceCache选项开启,则将cache落地。 if (options.code_cache_options == ShellOptions::CodeCacheOptions::kProduceCache) { // Serialize and store it in memory for the next execution. ScriptCompiler::CachedData* cached_data = ScriptCompiler::CreateCodeCache(script->GetUnboundScript()); StoreInCodeCache(isolate, source, cached_data); delete cached_data; } //【重点】运行js脚本 maybe_result = script->Run(realm); //处理选项,如果是kProduceCacheAfterExecute,则运行后在落地cache if (options.code_cache_options == ShellOptions::CodeCacheOptions::kProduceCacheAfterExecute) { // Serialize and store it in memory for the next execution. ScriptCompiler::CachedData* cached_data = ScriptCompiler::CreateCodeCache(script->GetUnboundScript()); StoreInCodeCache(isolate, source, cached_data); delete cached_data; } if (process_message_queue && !EmptyMessageQueues(isolate)) success = false; data->realm_current_ = data->realm_switch_; } Local<Value> result; //读取结果数据 if (!maybe_result.ToLocal(&result)) { DCHECK(try_catch.HasCaught()); // Print errors that happened during execution. if (report_exceptions) ReportException(isolate, &try_catch); return false; } DCHECK(!try_catch.HasCaught()); if (print_result) { //如果配置了test_shell选项,则把结果重定向到标准输出 if (options.test_shell) { //如果结果格式未定义,需要Stringify和格式化 if (!result->IsUndefined()) { // If all went well and the result wasn't undefined then print // the returned value. v8::String::Utf8Value str(isolate, result); fwrite(*str, sizeof(**str), str.length(), stdout); printf("\n"); } } else { v8::String::Utf8Value str(isolate, Stringify(isolate, result)); fwrite(*str, sizeof(**str), str.length(), stdout); printf("\n"); } } return success; }
0x04 小结
Shell::ExecuteString 函数说明了 v8 运行的大致流程,如果写 demo 可以参考这个函数。
全文完
本文由 简悦 SimpRead 优化,用以提升阅读体验 使用了 全新的简悦词法分析引擎 beta,点击查看详细说明0x00 前言0x01 调用栈0x02 SourceGroup::Execute 函数0x03 Shell::ExecuteString 函数0x04 小结
(4 条消息)V8 源码分析之 d8 源码注解 (第七篇)_counsellor 的专栏 - CSDN 博客_v8::internal::registerbase<v8::internal::register
0x00 前言
js 代码解析的过程为编译成字节码后再加载字节码执行, ScriptCompiler::Compile()
的过程是分为词法分析与语法分析,将 js 代码解析成 AST 树后就可以很顺利的转换成字节码。
本节先跳过复杂的编译过程看下执行逻辑。
0x01 调用栈
Thread 1 "d8" hit Breakpoint 1, v8::Shell::ExecuteString (isolate=0x59000000000, source=..., name=..., print_result=v8::Shell::kNoPrintResult, report_exceptions=v8::Shell::kReportExceptions, process_message_queue=v8::Shell::kProcessMessageQueue) at ../../src/d8/d8.cc:527 527 maybe_result = script->Run(realm); (gdb) s v8::Local<v8::Script>::operator-> (this=0x7fffffffd0a0) at ../../include/v8.h:213 213 V8_INLINE T* operator->() const { return val_; } (gdb) s v8::Script::Run (this=0x5555556c1b98, context=...) at ../../src/api/api.cc:2143 2143 auto isolate = reinterpret_cast<i::Isolate*>(context->GetIsolate()); (gdb) bt #0 v8::Script::Run (this=0x5555556c1b98, context=...) at ../../src/api/api.cc:2143 #1 0x00005555555efd77 in v8::Shell::ExecuteString (isolate=0x59000000000, source=..., name=..., print_result=v8::Shell::kNoPrintResult, report_exceptions=v8::Shell::kReportExceptions, process_message_queue=v8::Shell::kProcessMessageQueue) at ../../src/d8/d8.cc:527 #2 0x00005555555fd57e in v8::SourceGroup::Execute (this=0x5555556542a8, isolate=0x59000000000) at ../../src/d8/d8.cc:2620 #3 0x000055555560008b in v8::Shell::RunMain (isolate=0x59000000000, argc=2, argv=0x7fffffffdf38, last_run=true) at ../../src/d8/d8.cc:3100 #4 0x00005555556013a6 in v8::Shell::Main (argc=2, argv=0x7fffffffdf38) at ../../src/d8/d8.cc:3741 #5 0x00005555556016e2 in main (argc=2, argv=0x7fffffffdf38) at ../../src/d8/d8.cc:3777
0x02 Script::Run 函数
MaybeLocal<Value> Script::Run(Local<Context> context) { auto isolate = reinterpret_cast<i::Isolate*>(context->GetIsolate()); //如果开启--trace-events-enabled选项的话,则初始化TraceEvent相关的对象,进行堆栈,性能相关 //指标的跟踪,当然这里没有开启,可以忽略 TRACE_EVENT_CALL_STATS_SCOPED(isolate, "v8", "V8.Execute"); //日志记录V8开始执行,声明布尔型变量has_pending_exception用于保存执行返回结果 ENTER_V8(isolate, context, Script, Run, MaybeLocal<Value>(), InternalEscapableScope); //初始化直方图计时器 i::HistogramTimerScope execute_timer(isolate->counters()->execute(), true); //初始化聚合直方图计时器 i::AggregatingHistogramTimerScope timer(isolate->counters()->compile_lazy()); //初始化计时器事件 i::TimerEventScope<i::TimerEventExecute> timer_scope(isolate); //初始化一个js函数句柄fun auto fun = i::Handle<i::JSFunction>::cast(Utils::OpenHandle(this)); //初始化receiver i::Handle<i::Object> receiver = isolate->global_proxy(); //初始化一个js变量用于保存js的执行结果 Local<Value> result; //执行js代码 has_pending_exception = !ToLocal<Value>( i::Execution::Call(isolate, fun, receiver, 0, nullptr), &result); RETURN_ON_FAILED_EXECUTION(Value); RETURN_ESCAPED(result); }
TRACE_EVENT_CALL_STATS_SCOPED 宏
事件跟踪(trace event)是 v8 引擎的一个重要调试辅助的功能,事件有不同的分组 (category),例如堆栈调用,函数执行时间等等。
浏览器可以图形化这些事件日志,方便分析性能瓶颈。借用 V8 官网的一张图,同学们可以有个初步印象
回到源码上来,看下这个宏展开
#define TRACE_EVENT_CALL_STATS_SCOPED(isolate, category_group, name) \ INTERNAL_TRACE_EVENT_CALL_STATS_SCOPED(isolate, category_group, name)
继续展开
#define INTERNAL_TRACE_EVENT_CALL_STATS_SCOPED(isolate, category_group, name) \ INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \ v8::internal::tracing::CallStatsScopedTracer INTERNAL_TRACE_EVENT_UID( \ tracer); \ if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE()) { \ INTERNAL_TRACE_EVENT_UID(tracer) \ .Initialize(isolate, INTERNAL_TRACE_EVENT_UID(category_group_enabled), \ name); \ }
第一句
INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group);
看下宏定义
#define INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group) \ static TRACE_EVENT_API_ATOMIC_WORD INTERNAL_TRACE_EVENT_UID(atomic) = 0; \ const uint8_t* INTERNAL_TRACE_EVENT_UID(category_group_enabled); \ INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO_CUSTOM_VARIABLES( \ category_group, INTERNAL_TRACE_EVENT_UID(atomic), \ INTERNAL_TRACE_EVENT_UID(category_group_enabled));
宏定义太多了,就不一一介绍了,这段翻译成人话如下:
//初始化一个事件临时变量 static v8::base::AtomicWord trace_event_unique_atomic2144 = 0; //声明事件指针,为啥没有赋值? const uint8_t* trace_event_unique_category_group_enabled2144; //这里赋了初值,通过原子操作,原子操作的主要主用是避免多线程中读写错乱的问题 trace_event_unique_category_group_enabled2144 = reinterpret_cast<const uint8_t*>( v8::base::Relaxed_Load(&(trace_event_unique_atomic2144))); //判断事件指针 != NULL则进到block中执行 if (!trace_event_unique_category_group_enabled2144) { //获取TraceEventHelper的函数句柄,给事件指针 trace_event_unique_category_group_enabled2144 = v8::internal::tracing::TraceEventHelper::GetTracingController( )->GetCategoryGroupEnabled(trace_event_unique_category_group_enabled2144); v8::base::Relaxed_Store( &(trace_event_unique_atomic2144), (reinterpret_cast<v8::base::AtomicWord>( trace_event_unique_category_group_enabled2144))); }
第二句
v8::internal::tracing::CallStatsScopedTracer INTERNAL_TRACE_EVENT_UID( \ tracer); \
展开后
v8::internal::tracing::CallStatsScopedTracer trace_event_unique_tracer2144;
声明了一个 CallStatsScopedTracer 类型的 scope 状态跟踪器。
第三句
if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE()) { \ INTERNAL_TRACE_EVENT_UID(tracer) \ .Initialize(isolate, INTERNAL_TRACE_EVENT_UID(category_group_enabled), \ name); \ }
展开后
if( v8::base::Relaxed_Load(reinterpret_cast<const v8::base::Atomic8*>( trace_event_unique_category_group_enabled2144))& (1|4) ){ trace_event_unique_tracer2144.Initialize(isolate, trace_event_unique_category_group_enabled2144, name) }
分解如下:INTERNAL_TRACE_EVENT_CALL_STATS_SCOPED
中有一个 if 判断语句if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE())
比较关键。
看下 if 判断的内容
#define INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE() \ TRACE_EVENT_API_LOAD_CATEGORY_GROUP_ENABLED() & \ (kEnabledForRecording_CategoryGroupEnabledFlags | \ kEnabledForEventCallback_CategoryGroupEnabledFlags)
其中:
kEnabledForRecording_CategoryGroupEnabledFlags = 1
kEnabledForEventCallback_CategoryGroupEnabledFlags = 4
继续展开前面的宏TRACE_EVENT_API_LOAD_CATEGORY_GROUP_ENABLED()
#define TRACE_EVENT_API_LOAD_CATEGORY_GROUP_ENABLED() \ v8::base::Relaxed_Load(reinterpret_cast<const v8::base::Atomic8*>( \ INTERNAL_TRACE_EVENT_UID(category_group_enabled)))
INTERNAL_TRACE_EVENT_UID
宏的用处是:创建临时变量降低指令开销。这个变量名是由 name_prefix 和入口代码行号拼接组成的唯一名称,可以有效的避免冲突。
P.S. 说是降低指令开销,但完全想不出来降低什么了?你人工命名也不会多一条指令,最大的好处就是不用费脑想变量名了而已。
看看这组宏:
#define INTERNAL_TRACE_EVENT_UID3(a, b) trace_event_unique_##a##b #define INTERNAL_TRACE_EVENT_UID2(a, b) INTERNAL_TRACE_EVENT_UID3(a, b) #define INTERNAL_TRACE_EVENT_UID(name_prefix) \ INTERNAL_TRACE_EVENT_UID2(name_prefix, __LINE__)
拼接后的函数调用栈如下:
(gdb) bt #0 v8::base::Relaxed_Load (ptr=0x7ffff7fb3b08 <v8::Script::Run(v8::Local<v8::Context>)::trace_event_unique_atomic2144>) at ../../src/base/atomicops_internals_portable.h:199 #1 0x00007ffff66aafab in v8::Script::Run (this=0x5555556c1b98, context=...) at ../../src/api/api.cc:2144 #2 0x00005555555efd77 in v8::Shell::ExecuteString (isolate=0x167c00000000, source=..., name=..., print_result=v8::Shell::kNoPrintResult, report_exceptions=v8::Shell::kReportExceptions, process_message_queue=v8::Shell::kProcessMessageQueue) at ../../src/d8/d8.cc:527 #3 0x00005555555fd57e in v8::SourceGroup::Execute (this=0x5555556542a8, isolate=0x167c00000000) at ../../src/d8/d8.cc:2620 #4 0x000055555560008b in v8::Shell::RunMain (isolate=0x167c00000000, argc=2, argv=0x7fffffffdf38, last_run=true) at ../../src/d8/d8.cc:3100 #5 0x00005555556013a6 in v8::Shell::Main (argc=2, argv=0x7fffffffdf38) at ../../src/d8/d8.cc:3741 #6 0x00005555556016e2 in main (argc=2, argv=0x7fffffffdf38) at ../../src/d8/d8.cc:3777
trace_event_unique_atomic2144
确实是由固定字符串trace_event_unique_
+name_prefix 字符串atomic
和 frame 1 中的行号2144
组成。
TRACE EVENT 宏小结
主要用于事件追踪的注册,增加了多线程安全的原子操作保护。不是 LZ 关心的主要问题,不关心的同学也可以忽略掉。
0x03 小结
- 执行 js 前增加 trace event 和计时器帮助性能优化
- 用了很多宏
全文完
本文由 简悦 SimpRead 优化,用以提升阅读体验 使用了 全新的简悦词法分析引擎 beta,点击查看详细说明0x00 前言0x01 调用栈0x02 Script::Run 函数TRACE_EVENT_CALL_STATS_SCOPED 宏第一句第二句第三句TRACE EVENT 宏小结0x03 小结
标签:分析,Shell,context,..,isolate,v8,源码,d8,V8 来源: https://www.cnblogs.com/cx2016/p/13207941.html