《V8源码分析》
Posted cx2016
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《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 localhost
127.0.1.1 ubuntu
127.0.0.1 chrome-infra-packages.appspot.com
# The following lines are desirable for IPv6 capable hosts
::1 ip6-localhost ip6-loopback
fe00::0 ip6-localnet
f"..., 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 函数分析
路径
v8srcd8d8.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
全文完
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‘
", 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
");
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("
");
}
} else {
v8::String::Utf8Value str(isolate, Stringify(isolate, result));
fwrite(*str, sizeof(**str), str.length(), stdout);
printf("
");
}
}
return success;
}
0x04 小结
Shell::ExecuteString 函数说明了 v8 运行的大致流程,如果写 demo 可以参考这个函数。
全文完
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 和计时器帮助性能优化
- 用了很多宏
全文完
0x00 前言0x01 调用栈0x02 Script::Run 函数TRACE_EVENT_CALL_STATS_SCOPED 宏第一句第二句第三句TRACE EVENT 宏小结0x03 小结
以上是关于《V8源码分析》的主要内容,如果未能解决你的问题,请参考以下文章
鸿蒙源码分析系列(总目录) | 百万汉字注解 百篇博客分析 | 百篇博客分析鸿蒙源码 | v8.21
Android 插件化VirtualApp 源码分析 ( 目前的 API 现状 | 安装应用源码分析 | 安装按钮执行的操作 | 返回到 HomeActivity 执行的操作 )(代码片段
Android 逆向整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 dvmOptimizeDexFile() 方法分析 | /bin/dexopt 源码分析 )(代码片段
Android 事件分发事件分发源码分析 ( Activity 中各层级的事件传递 | Activity -> PhoneWindow -> DecorView -> ViewGroup )(代码片段