V8源码分析之d8源码注解(第七篇)

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. 用了很多宏
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值