ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

《V8源码分析》

2020-06-29 14:51:34  阅读:630  来源: 互联网

标签:分析 Shell context .. isolate v8 源码 d8 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 小结

  1. 执行 js 前增加 trace event 和计时器帮助性能优化
  2. 用了很多宏

全文完

本文由 简悦 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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有