您当前的位置: 首页 > 

顺其自然~

暂无认证

  • 3浏览

    0关注

    1317博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

CEF之多进程调试

顺其自然~ 发布时间:2021-01-14 14:03:48 ,浏览量:3

CEF3 使用多进程。其中包括:一个浏览器进程、一个渲染进程和若干其他进程(处理插件的进程、处理GUP的进程等)。

  • 浏览器进程处理窗口的创建和绘制,以及网络访问等,浏览器进程包含了应用程序的主要逻辑。
  • 渲染进程负责渲染 HTML 以及执行 JavaScript ,访问 DOM 等。
  • 其他进程则进行插件处理,GPU 处理等(如果有的话)。
  • 多个进程之间通过 IPC (Inter-Process Communication) 通信。

首先启动浏览器进程(Browser),然后启动渲染进程(Render),再启动其他进程。

默认情况下 CEF3 使用一个 exe 的多个实例来实现上述的多个进程。比如在 CEF3:用CEF3实现最简单的浏览器 中,在 WinMain 开始时会执行下面的代码:

    // CEF applications have multiple sub-processes (render, plugin, GPU, etc)
    // that share the same executable. This function checks the command-line and,
    // if this is a sub-process, executes the appropriate logic.
    int exit_code = CefExecuteProcess(main_args, NULL, NULL);
    if (exit_code >= 0)
    {
        // The sub-process has completed so return here.
        return exit_code;
    }

这里 CefExecuteProcess() 会根据不同的命令行参数来执行不同的进程,如果是浏览器进程,该函数立即返回,返回值为 -1。如果是其他进程,则在浏览器退出时才返回,返回值是一个大于0的数。 运行结果如下图,可以看到同一个 exe 运行了三个实例。这里写图片描述

也可以通过设置 CefSettings.browser_subprocess_path 来以不同的 exe 实现 CEF 的多个进程。下面通过示例说明这一点。

创建两个工程,一个命名为 BrowserProcess 作为浏览器进程,另一个命名为 BrowserSubProcess 作为其他子进程。工程的设置可以参见 CEF3:用CEF3实现最简单的浏览器。

在 BrowserProcess 工程下创建 main.cpp,编写如下代码。注意这里不再调用 CefExecuteProcess(),而是设置 CefSettings.browser_subprocess_path 的值来指定子进程路径。

#include "include/cef_app.h"
#include "include/cef_browser.h"
#include "include/cef_client.h"
#include "include/wrapper/cef_closure_task.h"
#include "include/wrapper/cef_helpers.h"
#include 

class MyClient : public CefClient, public CefLifeSpanHandler
{
public:
    virtual ~MyClient() {}
    virtual CefRefPtr GetLifeSpanHandler() override { return this; }
    virtual void OnBeforeClose(CefRefPtr browser) override { CefQuitMessageLoop(); }
private:
    IMPLEMENT_REFCOUNTING(MyClient);
};

class MyApp : public CefApp, public CefBrowserProcessHandler
{
public:
    virtual ~MyApp() {}
    virtual CefRefPtr GetBrowserProcessHandler() override { return this; }
    virtual void OnContextInitialized() override 
    {
        CEF_REQUIRE_UI_THREAD();
        CefWindowInfo window_info;
        window_info.SetAsPopup(NULL, "cefsimple");
        CefRefPtr client(new MyClient());
        CefString url = "http://www.baidu.com";
        CefBrowserSettings browser_settings;
        CefBrowserHost::CreateBrowser(window_info, client, url, browser_settings, NULL);
    }
private:
    IMPLEMENT_REFCOUNTING(MyApp);
};

int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) 
{
    CefMainArgs main_args(hInstance);
    CefSettings settings;
    settings.no_sandbox = true;
    // 获取子进程路径
    WCHAR subProcessPath[MAX_PATH] = { 0 };
    GetModuleFileNameW(hInstance, subProcessPath, MAX_PATH);
    *(wcsrchr(subProcessPath, L'\\') + 1) = L'\0';
    LPCWSTR SUB_PROCESS_NAME = L"BrowserSubProcess.exe";
    wcsncat_s(subProcessPath, MAX_PATH, SUB_PROCESS_NAME, wcslen(SUB_PROCESS_NAME));
    // 设置子进程路径
    cef_string_from_wide(subProcessPath, MAX_PATH, &settings.browser_subprocess_path);

    auto myApp = CefRefPtr(new MyApp());

    CefInitialize(main_args, settings, myApp.get(), NULL);
    CefRunMessageLoop();
    CefShutdown();

    return 0;
}

在 BrowserSubProcess 工程下创建 main.cpp,编写如下代码。代码相当简单,仅仅是调用了 CefExecuteProcess()。

#include "include/cef_app.h"
#include 

int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
    CefMainArgs main_args(hInstance);

    return CefExecuteProcess(main_args, NULL, NULL);
}

编译两个工程后,确保两个 exe 在同一目录下,运行,结果如下。可以看到运行了不同的 exe 。这里写图片描述

Chrome的多进程模型给DEBUG带来了很大的挑战。

一、如果你设置代码的断点,默认情况下,VS只会跟踪那些在主进程Browser代码中的那些断点。VS提供了"Attach To Process"的方法。比如当Render Process启动之后,可以用菜单"Debug"=>"Attach To Process"选项,选择那个新产生的进程,然后在你需要跟踪的代码处设置断点,就可以。但是这种方法,只能在子进程启动之后,才比较有效,如果我们想在子进程启动时,跟踪某些代码的执行,就没有办法了。

二、针对这个Chrome从源代码级别提供了支持。共有两种方法:

1. 用启动选项“--single-process“,以单进程的方法来启动Chrome。

2.用启动选项"--renderer-startup-dialog"和"--no-sandbox"。两个选项将会让子进程在启动之后,弹出一个模态对话框。之后在关闭这个对话框之后才可以继续运行代码。在这期间,我们可以用上述"Attach To Process"的方法来跟踪子进程代码的执行。

之所以要加上"--no-sandbox",是因为默认情况下Chrome的子进程的创建和启动是在sanbox中(也就说访问系统资源是严格限制的),无法显示模态对话框UI。

Render进程的主函数如下:

int RendererMain(const MainFunctionParams& parameters) {
  const CommandLine& parsed_command_line = parameters.command_line_;
  base::ScopedNSAutoreleasePool* pool = parameters.autorelease_pool_;
 
  // This function allows pausing execution using the --renderer-startup-dialog
  // flag allowing us to attach a debugger.
  // Do not move this function down since that would mean we can't easily debug
  // whatever occurs before it.
  HandleRendererErrorTestParameters(parsed_command_line);

HandleRenderErrorTestParameters函数会显示这个模态对话框。

// This function provides some ways to test crash and assertion handling
// behavior of the renderer.
static void HandleRendererErrorTestParameters(const CommandLine& command_line) {
  // This parameter causes an assertion.
  if (command_line.HasSwitch(switches::kRendererAssertTest)) {
    DCHECK(false);
  }
 
  // This parameter causes a null pointer crash (crash reporter trigger).
  if (command_line.HasSwitch(switches::kRendererCrashTest)) {
    int* bad_pointer = NULL;
    *bad_pointer = 0;
  }
 
  if (command_line.HasSwitch(switches::kRendererStartupDialog)) {
#if defined(OS_WIN)
    std::wstring title = l10n_util::GetString(IDS_PRODUCT_NAME);
    title += L" renderer";  // makes attaching to process easier
    ::MessageBox(NULL, L"renderer starting...", title.c_str(),
                 MB_OK | MB_SETFOREGROUND);
#elif defined(OS_LINUX)

 

关注
打赏
1662339380
查看更多评论
立即登录/注册

微信扫码登录

0.0401s