精华内容
下载资源
问答
  • 前面已经把V8源码download下来和build的过程都讲过了。这里不做过多赘述,只说说在win7下搭建环境与的的一些问题。 0x01 系统环境 名称 版本 位数 备注 操作系统 Windows 7 Professional 6.1.7601, Service...

    0x00 前言

    前面已经把V8源码download下来和build的过程都讲过了。这里不做过多赘述,只说说在win7下搭建环境与的的一些问题。

    0x01 系统环境

    名称 版本 位数 备注
    操作系统 Windows 7 Professional 6.1.7601, Service Pack 1 64 bit \
    Python 2.7.13 64 bit \

    没有全局代理的情况下

    0x02 设置代理

    打开cmd.exe 配置git代理

    配置poweshell脚本cipd.ps1的代理,这里在WebClient对象中加入了代理服务器地址http://127.0.0.2:8080

    try {
      echo "Downloading CIPD client for $Platform from $URL..."
      $FiddlerWP = New-Object System.Net.WebProxy "http://127.0.0.2:8080"
      $wc = (New-Object System.Net.WebClient)
      $wc.Headers.Add("User-Agent", $UserAgent)
      $wc.Proxy = $FiddlerWP
      try {
        $wc.DownloadFile($URL, $TmpPath)
      } catch {
        throw "Failed to download the file, check your network connection"
      }
    

    0x03 禁止depot_tools更新

    先谢郭嘉,git可以设置代理,然而,depot_tools和cipd这两个需要翻墙更新,搞了半天没有跟到发起连接的代码位置。这里先说明什么是cipd,再给出禁用更新的方法。

    CIPD(Chrome Infrastructure Package Deployment)

    CIPD 是包部署工具。由包注册和一个可以创建,更新,下载和安装的命令行客户端两部分组成。了解inux的童鞋是不是有点感觉了。对,就是一个apt-get一样的包管理工具,只不过是跟pip,npm,brew等负责chrome相关的包管理。【传送门

    cipd的包结构

    CIPD package具有一个package name(例如“ infra / tools / foo”)和一个内容寻址实例的列表(例如“ bec8e88201949be06b06174178c2f62b81e4008e”),其中软件包名称中的斜杠形成了软件包的层次结构,而一个实例是一个ZIP文件。

    depot_tools

    depot_tools 是用于Chromium开发的需要的一系列工具集合。这个定义相当于屁都没说。【传送门

    Chromium使用一个脚本集合,即dept_tools, 用于管理Chromium源码仓库与Chromium开发过程之间的交互。主要包括如下工具集:

    • gclient:管理svn和git checkout的工具。类似于repo_tool.

    其他子项就不一一介绍了,有个印象就成了,打算深入了解depot_tools的童鞋,可以使用此【传送门】。

    gclient

    gclient是一个python脚本,用于管理模块化依赖项的工作区,每个依赖项都是用独立的不同的的svn或git 拉取(checkout)。

    gclient这个要单独拉出来说说,大概意思是,项目比较大,用了很多第三方库,这些库有的是svn有的是git,不好傻瓜式管理。就拍脑袋搞了一个封装,管你用的什么,gclient可以完成仓库管理的各种功能,快活的很。

    特性:

    • 根据操作系统OS指定依赖
    • 根据不同仓库的父子依赖关系指定依赖
    • 变量可用于表示抽象概念
    • 提供一个checkout运行后即可调用的Hook
    • .gclient和DEPS这些python脚本,可以方便地增加和修改额外配置数据

    .gclient 文件

    一个master 文件。实际上是一个python脚本。它指定以下变量:

    • solutions: 指定需要拉取项目的字段数组。
    • hooks:checkout的钩子
    • target_os: OS以依赖列表
    • cache_dir: 主要针对bots,多个工作集使用单个git缓存. 参见 gclient.py --cache-dir

    每个项目在solutions数组中定义,同时包含可选的DEPS文件。.gclient文件通过gclient config <url>生成,或者手写。每个solustions条目是包含一些变量的字段:

    • name:checkout的路径
    • url:用于fetch/clone的远程仓库
    • custom_deps: 可选,定制的依赖
    • custom_vars: 可选,定制的变量
    • safesync_url:可选

    禁用depot_tools的方法

    1. 修改gclient.bat文件

    setlocal下面增加一行set DEPOT_TOOLS_UPDATE=0,如下

    @echo off
    :: Copyright (c) 2012 The Chromium Authors. All rights reserved.
    :: Use of this source code is governed by a BSD-style license that can be
    :: found in the LICENSE file.
    setlocal
    
    echo "--------->turn off the update<------"
    set DEPOT_TOOLS_UPDATE=0
    :: Shall skip automatic update?
    IF "%DEPOT_TOOLS_UPDATE%" == "0" GOTO :CALL_GCLIENT
    
    :: Synchronize the root d irectory before deferring control back to gclient.py.
    call "%~dp0update_depot_tools.bat" %*
    
    :CALL_GCLIENT
    :: Ensure that "depot_tools" is somewhere in PATH so this tool can be used
    :: standalone, but allow other PATH manipulations to take priority.
    set PATH=%PATH%;%~dp0
    
    :: Defer control.
    IF "%GCLIENT_PY3%" == "1" (
      :: TODO(1003139): Use vpython3 once vpython3 works on Windows.
      python3 "%~dp0gclient.py" %*
    ) ELSE (
      python "%~dp0gclient.py" %*
    )
    

    这样gclient就不会更新报错了

    D:\worksp\git>depot_tools\gclient sync  --force
    "--------->turn off the update<------"
    [P11288 09:27:15.448 client.go:309 W] RPC failed transiently. Will retry in 1s    {"error":"failed to send request: Post
    https://chrome-infra-packages.appspot.com/prpc/cipd.Repository/ResolveVersion: proxyconnect tcp: tls: first record does
    ot look like a TLS handshake", "host":"chrome-infra-packages.appspot.com", "method":"ResolveVersion", "service":"cipd.Re
    ository", "sleepTime":"1s"}
    [P11288 09:27:15.453 client.go:309 W] RPC failed transiently. Will retry in 1s    {"error":"failed to send request: Post
    https://chrome-infra-packages.appspot.com/prpc/cipd.Repository/ResolveVersion: proxyconnect tcp: tls: first record does
    ot look like a TLS handshake", "host":"chrome-infra-packages.appspot.com", "method":"ResolveVersion", "service":"cipd.Re
    ository", "sleepTime":"1s"}
    [P11288 09:27:15.458 client.go:309 W] RPC failed transiently. Will retry in 1s    {"error":"failed to send request: Post
    https://chrome-infra-packages.appspot.com/prpc/cipd.Repository/ResolveVersion: proxyconnect tcp: tls: first record does
    ot look like a TLS handshake", "host":"chrome-infra-packages.appspot.com", "method":"ResolveVersion", "service":"cipd.Re
    ository", "sleepTime":"1s"}
    终止批处理操作吗(Y/N)? y
    
    D:\worksp\git>depot_tools\gclient sync  --force
    "--------->turn off the update<------"
    

    做事还得做绝,发现fetch还会更新,继续修改fetch.bat, 直接注释掉更新

    @echo off
    :: Copyright (c) 2013 The Chromium Authors. All rights reserved.
    :: Use of this source code is governed by a BSD-style license that can be
    :: found in the LICENSE file.
    setlocal
    
    :: Synchronize the root directory before deferring control back to gclient.py.
    :: call "%~dp0\update_depot_tools.bat"
    
    :: Ensure that "depot_tools" is somewhere in PATH so this tool can be used
    :: standalone, but allow other PATH manipulations to take priority.
    set PATH=%PATH%;%~dp0
    
    :: Defer control.
    IF "%GCLIENT_PY3%" == "1" (
      vpython3 "%~dp0\fetch.py" %*
    ) ELSE (
      vpython "%~dp0\fetch.py" %*
    )
    

    2. 使用环境变量

    据说在环境变量中设置DEPOT_TOOLS_UPDATE为0 即可。第一种方法亲测有效,这个仅供参考。

    0x04 fetch v8

    执行如下命令

    depot_tools\fetch v8
    

    提示没有权限

    Running: 'C:\Users\xxxx\Scripts\python.exe' 'D:\worksp\git\depot_tools\gclient.py' sync -
    -with_branch_heads
    Running: git submodule foreach 'git config -f $toplevel/.git/config submodule.$name.ignore all'
    Traceback (most recent call last):
      File "D:\worksp\git\depot_tools\\fetch.py", line 318, in <module>
        sys.exit(main())
      File "D:\worksp\git\depot_tools\\fetch.py", line 313, in main
        return run(options, spec, root)
      File "D:\worksp\git\depot_tools\\fetch.py", line 307, in run
        return checkout.init()
      File "D:\worksp\git\depot_tools\\fetch.py", line 158, in init
        cwd=wd)
      File "D:\worksp\git\depot_tools\\fetch.py", line 111, in run_git
        return git_common.run(*cmd, **kwargs)
      File "D:\worksp\git\depot_tools\git_common.py", line 700, in run
        return run_with_stderr(*cmd, **kwargs)[0]
      File "D:\worksp\git\depot_tools\git_common.py", line 766, in run_with_stderr
        proc = subprocess2.Popen(cmd, **kwargs)
      File "D:\worksp\git\depot_tools\subprocess2.py", line 159, in __init__
        % (str(e), kwargs.get('cwd'), args[0]))
    OSError: Execution failed with error: [Error 267] .
    Check that D:\worksp\git\v8 or E:\Program Files\Git\cmd\git.exe exist and have execution permission.
    

    另一个坑

    fetch.py脚本不知道为何在win32环境下会使用git.bat命令而不用git,导致刚开始就报错

    >depot_tools\fetch v8
     --spec 'solutions = [
      {
        "url": "https://chromium.googlesource.com/v8/v8.git",
        "managed": False,
        "name": "v8",
        "deps_file": "DEPS",
        "custom_deps": {},
      },
    ]
    '
    v8 (ERRO
    R)
    ----------------------------------------
    [0:00:00] Started.
    ----------------------------------------
    Traceback (most recent call last):
      File "D:\worksp\git\depot_tools\metrics.py", line 267, in print_notice_and_exit
        yield
      File "D:\worksp\git\depot_tools\gclient.py", line 3183, in <module>
        sys.exit(main(sys.argv[1:]))
      File "D:\worksp\git\depot_tools\gclient.py", line 3168, in main
        return dispatcher.execute(OptionParser(), argv)
      File "D:\worksp\git\depot_tools\subcommand.py", line 252, in execute
        return command(parser, args[1:])
      File "D:\worksp\git\depot_tools\gclient.py", line 2725, in CMDsync
        ret = client.RunOnDeps('update', args)
      File "D:\worksp\git\depot_tools\gclient.py", line 1763, in RunOnDeps
        patch_refs=patch_refs, target_branches=target_branches)
      File "D:\worksp\git\depot_tools\gclient_utils.py", line 914, in flush
        reraise(e[0], e[1], e[2])
      File "D:\worksp\git\depot_tools\gclient_utils.py", line 991, in run
        self.item.run(*self.args, **self.kwargs)
      File "D:\worksp\git\depot_tools\gclient.py", line 933, in run
        file_list)
      File "D:\worksp\git\depot_tools\gclient_scm.py", line 132, in RunCommand
        return getattr(self, command)(options, args, file_list)
      File "D:\worksp\git\depot_tools\gclient_scm.py", line 516, in update
        mirror = self._GetMirror(url, options, revision_ref)
      File "D:\worksp\git\depot_tools\gclient_scm.py", line 957, in _GetMirror
        if not self.cache_dir:
      File "D:\worksp\git\depot_tools\gclient_scm.py", line 218, in cache_dir
        return git_cache.Mirror.GetCachePath()
      File "D:\worksp\git\depot_tools\git_cache.py", line 305, in GetCachePath
        ['cache.cachepath']).strip()
      File "E:\Program Files\Python\lib\subprocess.py", line 567, in check_output
        process = Popen(stdout=PIPE, *popenargs, **kwargs)
      File "E:\Program Files\Python\lib\subprocess.py", line 711, in __init__
        errread, errwrite)
      File "E:\Program Files\Python\lib\subprocess.py", line 959, in _execute_child
        startupinfo)
    WindowsError: [Error 2]
    

    直接修改git_cache.py文件,使git_exe变量赋值为'git',代码如下:

    class Mirror(object):
      git_exe = 'git' if sys.platform.startswith('win') else 'git'
      gsutil_exe = os.path.join(
        os.path.dirname(os.path.abspath(__file__)), 'gsutil.py')
      cachepath_lock = threading.Lock()
    

    继续fetch,可以看到已经能正常下载了

    1>git config
    1>________ running 'git -c core.deltaBaseCacheLimit=2g clone --no-checkout --progress https://chromium.googlesource.com/v
    8/v8.git D:\worksp\git\_gclient_v8_prggkr' in 'D:\worksp\git'
    1>Cloning into 'D:\worksp\git\_gclient_v8_prggkr'...
    1>remote: Sending approximately 581.51 MiB ...
    1>remote: Counting objects: 7675, done
    1>remote: Finding sources: 100% (15/15)
    1>Receiving objects:  13% (96685/723669), 41.43 MiB | 268.00 KiB/s
    

    0x05问题

    1. gclient工具目前只支持python2

    如果使用python3的话会报错,换成python2的环境即可。

    Warning: gclient doesn't yet support Python 3 and may not work correctly.
    

    0x06 参考文献

    http://www.cclk.cc/2017/08/03/webrtc/webrtc学习1-全平台编译/
    https://blog.csdn.net/yyinhai/article/details/52955608
    https://blog.csdn.net/yyinhai/article/details/54016892
    https://skylerlee.github.io/codelet/2017/03/08/build-v8/

    展开全文
  • js代码解析的过程为编译成字节码后再加载字节码执行, ScriptCompiler::Compile()的过程是分为词法分析与语法分析,将js代码解析成AST树后就可以很顺利的转换成字节码。 本节先跳过复杂的编译过程看下执行逻辑。 0x...

    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. 用了很多宏
    展开全文
  • d8自己封装了一个js代码执行器,上一篇我们的代码执行到options.isolate_sources[0].Execute(isolate,本文将做进一步分析。 0x01 调用栈 #0 v8::SourceGroup::Execute (this=0x5555556542a8, isolate=0x1e9d...

    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可以参考这个函数。

    展开全文
  • Handle是使用v8的时候很重要的一个概念和类。他本质是堆对象的封装。我们通过Handle管理真正的对象,而不是直接操作对象。Handle在v8中有两个实现。一个是对外使用的一个是内部使用的。我们先看一下内部使用的。 1 ...

    Handle是使用v8的时候很重要的一个概念和类。他本质是堆对象的封装。我们通过Handle管理真正的对象,而不是直接操作对象。Handle在v8中有两个实现。一个是对外使用的一个是内部使用的。我们先看一下内部使用的。

    1 内部handle

    template<class T>
    class Handle {
     public:
      INLINE(Handle(T** location))  { location_ = location; }
      INLINE(explicit Handle(T* obj));
    
      INLINE(Handle()) : location_(NULL) {}
      
      template <class S> Handle(Handle<S> handle) {
        location_ = reinterpret_cast<T**>(handle.location());
      }
    
      INLINE(T* operator ->() const)  { return operator*(); }
    
      bool is_identical_to(const Handle<T> other) const {
        return operator*() == *other;
      }
      
      INLINE(T* operator*() const);
    
      T** location() const {
        return location_;
      }
    
      template <class S> static Handle<T> cast(Handle<S> that) {
        T::cast(*that);
        return Handle<T>(reinterpret_cast<T**>(that.location()));
      }
    
      static Handle<T> null() { return Handle<T>(); }
      bool is_null() {return location_ == NULL; }
    
      inline Handle<T> EscapeFrom(HandleScope* scope);
    
     private:
      T** location_;
    };
    

    下面是实现。

    template<class T>
    Handle<T>::Handle(T* obj) {
      location_ = reinterpret_cast<T**>(HandleScope::CreateHandle(obj));
    }
    
    
    template <class T>
    inline T* Handle<T>::operator*() const {
      return *location_;
    }
    

    Handle类的定义没有太多的逻辑,就是对用户定义的对象指针进行封装。有一个重要的地址是构造函数。我们看到当我们定义一个Handle的时候,他会调HandleScope::CreateHandle生成一个Handle对象。在HandleScope那篇文章已经分析过了。handle对象的location对象指针一个内存,该内存保存了obj的地址。

    2 外部handle

    // T表示handle管理的对象的类型
    template <class T> class Handle {
     public:
    
      Handle();
    
      explicit Handle(T* val) : val_(val) { }
    
      // *that得到指向handle管理的对象的指针,转成T类型,赋值给val_
      template <class S> inline Handle(Handle<S> that)
          : val_(reinterpret_cast<T*>(*that)) {
       
        TYPE_CHECK(T, S);
      }
    
      bool IsEmpty() { return val_ == 0; }
    
      T* operator->();
    
      T* operator*();
    
      void Clear() { this->val_ = 0; }
    
      /*
        比较handle指向的对象的地址是否相等
        this是指向当前对象的指针,*this是当前对象,**this是返回val_的值,看重载运算符*的实现
        *that是val_的值
      */
      template <class S> bool operator==(Handle<S> that) {
        void** a = reinterpret_cast<void**>(**this);
        void** b = reinterpret_cast<void**>(*that);
        // a等于0,则返回b是否等于0,是的话说明a==b,即true
        if (a == 0) return b == 0;
        // a不等于0,如果b==0,则返回false
        if (b == 0) return false;
        // 比较ab,即取val_里的内容比较
        return *a == *b;
      }
    
      template <class S> bool operator!=(Handle<S> that) {
        return !operator==(that);
      }
    
      template <class S> static inline Handle<T> Cast(Handle<S> that) {
        // 返回一个空的handle,即val_是null
        if (that.IsEmpty()) return Handle<T>();
        // *that得到指向handle管理的对象的指针,转成T类型的对象,转成底层对象是类型T的handle
        return Handle<T>(T::Cast(*that));
      }
    
     private:
      T* val_;
    };
    

    下面是实现。只有两个运算符的重载。

    template <class T>
    T* Handle<T>::operator->() {
      return val_;
    }
    
    
    template <class T>
    T* Handle<T>::operator*() {
      return val_;
    }
    

    我们看到Handle的实现没有太内容,就是在对象和用户之前加了一层。下面看看他的两个子类Local和Persistent。

    3 Local

    1 Local类是基于栈分配的一种Handle,他在一个函数开始的时候,声明一个HandleScope,HandleScope下面所有的Handle都在最近的的HandleScope中分配,函数执行完后,会一起被释放。

    template <class T> class Local : public Handle<T> {
     public:
      Local();
      // 调用Local函数的时候S被替换成that对应的类型,结果是Handle底层的val_指向一个T类型的对象
      template <class S> inline Local(Local<S> that)
          // *that即取得他底层对象的地址
          : Handle<T>(reinterpret_cast<T*>(*that)) {
        TYPE_CHECK(T, S);
      }
      template <class S> inline Local(S* that) : Handle<T>(that) { }
      template <class S> static inline Local<T> Cast(Local<S> that) {
        if (that.IsEmpty()) return Local<T>();
        return Local<T>(T::Cast(*that));
      }
    
      static Local<T> New(Handle<T> that);
    };
    

    Local没有做什么事情,是对基类Handle的简单继承。下面是实现。

    template <class T>
    Handle<T>::Handle() : val_(0) { }
    
    template <class T>
    Local<T>::Local() : Handle<T>() { }
    
    template <class T>
    Local<T> Local<T>::New(Handle<T> that) {
      if (that.IsEmpty()) return Local<T>();
      void** p = reinterpret_cast<void**>(*that);
      return Local<T>(reinterpret_cast<T*>(HandleScope::CreateHandle(*p)));
    }
    

    我们看看如果使用一个句柄。

    HandleScope scope;
    Local<String> source = String::New('hello');
    

    我们看一下String::New的实现。

    // i::Handle表示内部使用的handle
    Local<String> v8::String::New(const char* data, int length) {
      if (length == -1) length = strlen(data);
      // 申请一个对象,由handle管理
      i::Handle<i::String> result = i::Factory::NewStringFromUtf8(i::Vector<const char>(data, length));
      return Utils::ToLocal(result);
    }
    
    Local<v8::String> Utils::ToLocal(v8::internal::Handle<v8::internal::String> obj) { 
    	return Local<String>(reinterpret_cast<String*>(obj.location())); 
    }
    

    我们在看下HandleScope中的那个图。再来例假ToLocal函数的逻辑。
    在这里插入图片描述
    我们对着图来理解ToLocal,我们知道obj.location()返回的是指针,指向保存了对象地址的内存地址。然后转成String*,即拿到对象的地址。构造一个Local对象返回。即Local内部管理用户定义的对象(String::New函数执行完后,他里面定义的result,即handle被析构)。如下图。
    在这里插入图片描述
    当HandleScope析构的时候,他会释放用户定义的对象的内存,然后Local对象本身是在栈上分配的,也会被析构。这就是v8用本地handle(临时handle)管理堆对象的大致原理。一般来说handle在函数结束后就会被释放,如果想在函数执行完还使得句柄可用,可用使用逃逸(escape)。原理是销毁当前的HandleScope,然后在前一个HandleScope对象里分配一个handle。下面继续看看持久句柄。
    4 Persisten

    template <class T> class Persistent : public Handle<T> {
     public:
    
      Persistent();
    
      template <class S> inline Persistent(Persistent<S> that): Handle<T>(reinterpret_cast<T*>(*that)) {
        TYPE_CHECK(T, S);
      }
    
      template <class S> inline Persistent(S* that) : Handle<T>(that) { }
    
      template <class S> explicit inline Persistent(Handle<S> that)
          : Handle<T>(*that) { }
    
      template <class S> static inline Persistent<T> Cast(Persistent<S> that) 		{
        if (that.IsEmpty()) return Persistent<T>();
        return Persistent<T>(T::Cast(*that));
      }
    
      static Persistent<T> New(Handle<T> that);
    
      void Dispose();
    
      void MakeWeak(void* parameters, WeakReferenceCallback callback);
      void ClearWeak();
      bool IsNearDeath();
      bool IsWeak();
    
     private:
      friend class ImplementationUtilities;
      friend class ObjectTemplate;
    };
    

    相对于基类Handle,Persistent多了几个功能,我们看一下使用用例。

    Persistent<Context> context = Context::New();
    

    我们看一下Context::New()的定义。

    Persistent<Context> v8::Context::New(v8::ExtensionConfiguration* extensions,
                                         v8::Handle<ObjectTemplate> global_template,
                                         v8::Handle<Value> global_object) {
    
      i::Handle<i::Context> env = i::Bootstrapper::CreateEnvironment(
          Utils::OpenHandle(*global_object),
          global_template, extensions
      );
    
      return Persistent<Context>(Utils::ToLocal(env));
    }
    

    我们看一下CreateEnvironment的实现。

    Handle<Context> Bootstrapper::CreateEnvironment(...参数) {
      Genesis genesis(global_object, global_template, extensions);
      return genesis.result();
    }
    
    Genesis::Genesis(...参数) {
      CreateRoots(global_template, global_object);
      result_ = global_context_;
     }
     
    void Genesis::CreateRoots(...参数) {
      // 创建一个全局上下文对象,分配一个Context对象
      global_context_ =
          Handle<Context>::cast(
              GlobalHandles::Create(*Factory::NewGlobalContext()));
    }
    
    Handle<Context> result() { return result_; }
    

    通过上面的代码我们知道Persistent指向的是一个GlobalHandles::Create返回的地址。所以我们主要分析GlobalHandles这个类的实现。这个类的代码比较多,我们只分析相关的(Node类维护一个对象的信息,地址,状态)。后面会单独分析。

    // 一个handle对应一个Node
    Handle<Object> GlobalHandles::Create(Object* value) {
      Counters::global_handles.Increment();
      Node* result;
      /*
        有一个free_list,保存着DESTROYED状态但还没有被释放的Node,
        first_free指向第一个节点,为NULL说明没有待回收的节点,即没有可重用的节点 
      */
      if (first_free() == NULL) {
        // Allocate a new node.
        // 没有可重用的节点则分配一个新的
        result = new Node(value);
        // 头插法,设置新增的node的下一个节点是当前头结点
        result->set_next(head());
        // 头指针指向新增的node
        set_head(result);
      } else {
        // Take the first node in the free list.
        // 获取一个可以重用的节点
        result = first_free();
        // 获取重用节点在free_list中的第一个节点,first_free指向新的可重用节点 
        set_first_free(result->next_free());
        // 重新初始化该节点
        result->Initialize(value);
      }
      // 返回Node对象的首地址
      return result->handle();
    }
    

    从上面的代码中我们大概知道,有一个链表,每个node节点保存了一个持久对象的信息。持久句柄指向的对象都是在这个链表里管理的。持久句柄执行node的地址,最后要调Dispose释放。

    template <class T>
    void Persistent<T>::Dispose() {
      if (this->IsEmpty()) return;
      V8::DisposeGlobal(reinterpret_cast<void**>(**this));
    }
    
    void V8::DisposeGlobal(void** obj) {
      LOG_API("DisposeGlobal");
      if (has_shut_down) return;
      i::GlobalHandles::Destroy(reinterpret_cast<i::Object**>(obj));
    }
    
    // 销毁一个节点
    void GlobalHandles::Destroy(Object** location) {
      Counters::global_handles.Decrement();
      if (location == NULL) return;
      Node* node = Node::FromLocation(location);
      node->Destroy();
      // Link the destroyed.
      // 设置待销毁节点在free_list链表里的下一个节点是当前的头结点
      node->set_next_free(first_free());
      // 头指针指向待销毁的节点,
      set_first_free(node);
    }
    

    大致是根据对象的地址转成node节点,销毁该节点。从链表中删除。

    总结,这就是v8中关于handle的一些知识。

    展开全文
  • 本文获取源码的前提是科学上网,请自行解决。 0x01 使用Git V8的Git仓库为 https://chromium.googlesource.com/v8/v8.git,同时在GitHub上还有个官方镜像: https://github.com/v8/v8. 不管是为了遵循主流思想在git.....
  • V8引擎使用Ninja进行构建源码,GN是用来辅助生成Ninja配置文件的工具。 从源码构建V8需要三个步骤: 生成构建所需文件( generating build files) 编译(compiling) 运行测试用例(running tests) 官方提供两种...
  • 0x00 前言 没了你,我颓废了自己。心里那些苦,都只哽在喉咙里,一想起来就泪如雨下。 ----王国维 ...0x00007ffff4a8ea44 in v8::base::LocalKeyToPthreadKey (local_key=32767) at ../../src/base...
  • GlobalHandles是实现v8中持久句柄功能的类。GlobalHandles主要是维护一个链表,每个节点维护堆对象的状态。我们先看看节点的定义。 class GlobalHandles::Node : public Malloced { public: void Initialize...
  • v8里有smi保存整形,但是他只有31位,超过31位的就需要用HeapNumber。 // 存储了数字的堆对象 class HeapNumber: public HeapObject { public: inline double value(); inline void set_value(double value); ...
  • 1 v8的对象是4字节对齐的,用地址的低两位出来标记对象的类型。 2 堆对象(HeapObject)是Object的子类。Object里面的很多方法都是用于堆对象。堆对象有自己的一套对象类型判断方式。每个堆对象有一个map属性,他...

空空如也

空空如也

1 2 3 4 5 ... 12
收藏数 237
精华内容 94
关键字:

v8源码分析