Blink/Webkit浏览器内核崩溃分析过程总结

前两天技术团队旺旺群里有同事提出一个问题:在debug模式下,Chromium浏览器打开http://product.suning.com/125073744.html页面renderer进程就立即崩溃。团队对于Blink/Webkit内核问题的分析经验不多,故把分析过程写出一个总结,希望对大家以后分析此类问题有帮助。 多进程调试 Chromium浏览器是多进程的架构。而Blink内核在renderer进程,debug模式下,VS调试不能抓到renderer进程的异常,也就无法定位问题。所以解决问题的第一步是把VS调试器附加到问题相应的renderer进程。 Chromium已有一篇文档讲述如何调试Chromium。对于浏览器子进程的调试,有一个–wait-for-debugger-children的命令行参数。给浏览器传递这个命令行参数之后,生成的子进程都会在一开始等待调试器60秒附加上去。另外这个命令行还可以指定是plugin进程还是renderer进程。因为这次调试的对象是renderer进程,故我们在VS调试器设置–wait-for-debugger-children=renderer传递给被调试的浏览器。 重现问题 用VS打开Chromium的源代码工程,并在debug模式下运行。启动浏览器之后,新建tab页,在地址栏里输入会导致renderer进程崩溃的网址http://product.suning.com/125073744.html。这时候就是生成对应的renderer进程。这个renderer进程在60秒等待我们把调试器附加上去。 我们找到新建renderer进程的PID,然后用VS的debug->Attach... Read More | Share it now!

Chromium WebUI机制介绍

本文简要介绍一些Chromium WebUI的特点,然后分析其内部机制,最后介绍如何创建使用WebUI。 WebUI介绍 WebUI是通过本地的html网页来实现客户端的UI功能,把c++、html、css、js组织在一起,css,html做界面,js和c++做逻辑,灵活而又强大。chromium UI里面很多地方使用WebUI ,比如设置页面,flags开关页面,关于页面。我们在浏览器的地址栏中输入chrome://chrome-urls/,就可以看到更多的WebUI 链接页面。 Chromium之所以大量用到WebUI,我想有两个主要原因: WebUI界面看起来就像是新建了一个tab打开的一个网页,UI交互非常统一,也非常简洁 利用html构建UI,减少了开发跨平台UI的复杂性 WebUI机制分析 我们现在以chromium中一个简单而又典型WebUI页面chrome://version/为例子分析WebUI机制。 创建WebUI流程 在地址栏中输入chrome://version/与输入一个网址创建新标签页的基本流程是一致的。在地址栏中输入任何网址WebContentsImpl都会创建WebUIImpl 。如果在GetWebUIFactoryFunction中找到对应的chrome::kChromeUIVersionHost,就会创建对应的WebUIController——VersionUI,然后WebContentsImpl::CreateWebUI会判断WebUIController是否创建成功,如果创建失败则销毁WebUIImpl。 资源加载流程 WebUI的资源加载也是复用一般的网页网络请求机制,不同的是一般的http协议底层是用URLRequestHttpJob去做网络请求,而WebUI则是使用URLRequestChromeJob。 在地址栏输入一个WebUI网址chrome://version/,就会创建VersionUI对象,VersionUI会创建一个与当前host绑定的WebUIDataSource,并添加到URLDataManager中。当发起网络请求,URLDataSourceImpl根据host返回对应的资源,然后再当做response返回。 类图介绍 ChromeWebUIControllerFactory是创建WebUI对应的WebUIController。 VersionUI实现了WebUIController,会根据WebUI的网址创建一个WebUIDataSource。WebUIDataSource就可以添加WebUI所需要的前端资源文件。VersionUI同时也创建VersionHandler并添加到WebUI中。 VersionHandler继承自WebUIMessageHandler。通过WebUI的RegisterMessageCallback来注册回调,VersionHandler就可以处理来自前端的js的调用。 其实VersionHandler并非必须要创建,任何类都可以通过WebUI来注册js回调函数。WebUI类就是用来连接js与c++交互的纽带。 c++与js交互 c++调用js非常简单,直接通过WebUI的CallJavascriptFunction来执行js代码中的函数,比如: CallJavascriptFunction底层其实是调用的通用的RenderFrameHost接口的ExecuteJavaScript。 Js调用c++稍微复杂一点。 首先browser进程中通过WebUI的RegisterMessageCallback来向js暴露自己可被调用的接口,实现对应接口处理函数。之后js就可以通过chrome.send方法去调用c++中的接口。 在renderer进程里面WebUIExtension::Install通过v8创建一个chrome的js对象,并暴露出send,getVariableValue两个接口,注册接口的c++回调函数。js直接调用chrome.send,然后在renderer进程中对应的回调函数通过IPC把函数调用相关的信息发送到browser进程中的WebUIImpl中。 前端资源 WebUI的前端html网页元素扩展了三个属性:i18n-content、i18n-options、i18n-values。 i18n-content可以用来指定元素的文本内容。比如我们在c++中指定application_label的字符串内容为Google Chrome,html网页写成<td class=”label” i18n-content=”application_label”></td>,最终渲染出的html为<td class=”label” i18n-content=”application_label”>Google Chrome</td>。 i18n-options会生成一个<select>的<option>元素,具体用法搜索现有代码中的例子。 i18n-values会生成一个元素的属性和值的列表。比如<html id=”t” i18n-values=”dir:textdirection;lang:language”>。 扩展属性中的值是c++把数据生成strings.js,然后网页再从strings.js中加载。 创建WebUI chromium有篇文档介绍如何创建WebUI界面。但这个文档有些过时,有些地方已经改变了。我们通过创建一个计算两个整数和的WebUI例子来学习如何使用WebUI。 在这个例子中,我们打开chrome://calculator/页面,会有两个输入框让我们输入两个整数,点击等号按钮,前端获取到输入的两个整数,然后调用c++的cpp_add函数计算出和,然后c++再调用js里面的set_result方法设置结果到输入框。 添加前端资源 WebUI的前端资源文件一般放在src/chrome/browser/resources目录下面,我们在这个目录下新增一个如下内容的calculator.html文件: c++代码在创建WebUIDataSource中可以指定这个标示符对应的字符串值。 最终chromium会渲染出html网页,其中就会把IDS_CALCULATOR_TITLE字符串的值根据calculator_title放到title里面,最终网页渲染出如下效果: 还有点注意的是html中引用了很多框架的js文件,其中load_time_data.js要放在最前面,i18n_template2.js要放在body里面。 然后再添加一个如下内容的calculator.js文件: 然后在src/chrome/app/generated_resources.grd加入一些本地化的字符串资源: 然后把新增前端文件加入到grd文件中,根据之前文件的惯例,我们加入到src/chrome/browser/browser_resources.grd文件中: 增加c++处理逻辑 增加url 首先,我们定义WebUI的url,在src/chrome/common/url_constants.h中定义: 然后在src/chrome/common/url_constants.cc中定义: 然后创建我们的WebUIController,我们定义CalculatorUI: 增加WebUI c++实现 CalculatorUI的实现如下: CalculatorUI会调用CreateCalculatorUIDataSource创建WebUIDataSource: 创建WebUIDataSource要跟指定的WebUI的host绑定到一起。AddLocalizedString是添加一些本地话字符串,AddString是添加ascii字符串,SetJsonPath是设置生成数据的js文件名。AddResourcePath是添加其他js资源,SetDefaultResource是添加html资源。 CalculatorUI也会在构造函数里增加WebUIMessageHandler类CalculatorHandler,在这个类里面处理c++与js的交互: 然后把新增的c++代码文件加入到src/chrome/chrome_browser_ui.gypi中: 最终效果 最后我们打开chrome://calculator/,在第一第二个输入输入数字,然后点击等号按钮,就会在第三个输入框里得到前面输入的两个数字的和。 ... Read More | Share it now!

chromium browser端启动逻辑

chromium浏览器的入口是chrome_exe_main_win.cc,wWinMain主函数就在这个文件里。wWinMain里面的逻辑很简单,里面MainDllLoader会根据命令行type参数去加载chrome.dll或者chrome_child.dll,然后调用dll中的ChromeMain函数。   在ChromeMain中会创建ChromeMainDelegate对象,生成ContentMainParams传递给content::ContentMain,再通过ContentMainParams去初始化ContentMainRunner对象。 ChromeMainDelegate继承自content::ContentMainDelegate,主要是在围绕Sandbox初始化的前后做一些工作,另外创建一些ChromeContentBrowserClient,ChromeContentRendererClient,ChromeContentPluginClient,ChromeContentUtilityClient的对象,浏览器的更多逻辑都在ChromeContent*Client这些对象中。此外还调用content::SetContentClient设置ChromeContentClient。 ChromeMainDelegate的那些方法都会在ContentMainRunner中陆续调用到。 在ContentMainRunner::Initialize函数中又调用ChromeMainDelegate::BasicStartupComplete。ChromeMainDelegate::BasicStartupComplete做一些初始化工作并调用content::SetContentClient。 ContentMainRunner::Initialize又调用ContentClientInitializer通过ChromeMainDelegate去CreateContent*Client。然后ContentMainRunner::Initialize继续做一些初始化工作。 ContentMainRunner::Initialize中接着调用ChromeMainDelegate::PreSandboxStartup。 PreSandboxStartup中初始化UserDataDir。初始化logging系统。 ContentMainRunner::Initialize中接着初始化Sandbox。初始化工作完成。 然后调用ContentMainRunner::Run,在run里面调用RunNamedProcessTypeMain。RunNamedProcessTypeMain先调用ChromeMainDelegate::RunProcess,然后根据进程类型调用对应的BrowserMain,RendererMain,PluginMain,GpuMain等等。 在BrowserMain中,有个BrowserMainRunner,跟ContentMainRunner类似。BrowserMainRunner::Initialize中根据MainFunctionParams创建BrowserMainLoop。BrowserMainLoop::Init中调用ChromeContentBrowserClient::CreateBrowserMainParts创建BrowserMainParts*。同时还在这里Initialize创建其他线程消息循环。 最后BrowserMainRunnerImpl::Run调用BrowserMainLoop::RunMainMessageLoopParts开始了UI线程的循环。 ChromeContentBrowserClient是浏览器browser逻辑的枢纽。ChromeContentBrowserClient::CreateBrowserMainParts里面创建BrowserMainParts。BrowserMainParts又可以添加其他的ChromeBrowserMainExtraPartsViews,ChromeBrowserMainExtraPartsAsh,ChromeBrowserMainExtraPartsAura对象,这些对象封装的逻辑在BrowserMainRunner里面中被调用执行。 ChromeBrowserMainParts继承自BrowserMainParts,它是个非常总要的类。它创建了BrowserProcessImpl、StartupBrowserCreator、ChromeProcessSingleton、Profile、PrefService等对象。BrowserProcessImpl创建了各种Manager,Service,还有IO线程。StartupBrowserCreator,StartupBrowserCreatorImpl则是开始创建一个Browser,并初始化Profile。 Profile,ProfileImpl继承自content::BrowserContext,对应着browser回话上下文信息和用户配置信息。PrefService则是能把Profile里面的状态增删改查并保存到文件中。 OffTheRecordProfileImpl也是继承自Profile,它是用于隐身模式的浏览器上下文和用户配置信息。 WebContents代表着浏览器tab页中渲染出来的网页。WebContentsImpl则进一步继承了更多的网页方面的操控。Browser则是继承自WebContentsDelegate,它会收到来自WebContents的变化并做出响应。 ... Read More | Share it now!

chromium中的性能优化工具syzyProf

函数性能分析工具SyzyProf 我先开始介绍SyzyProf。这个工具可以捕获每个线程调用每个函数执行的时间,然后把结果生成一个KCacheGrind能够识别的数据格式文件,然后通过KCacheGrind的展示结果。你就可以知道函数哪个函数执行了次数最多,消耗的时间最多,哪个线程在读写文件,哪个线程在创建窗口界面,而且KCacheGrind以图形的方式显示出函数调用链等信息,非常直观。如图这是我生成CEF自带demo程序的函数调用信息。 其实能够函数性能分析工具已经够多了,比如xperf,AQTime,还有visual... Read More | Share it now!

使用vmmap.exe分析进程内存使用

参考:http://technet.microsoft.com/zh-cn/sysinternals/dd535533 vmmap是一个进程的虚拟和物理内存分析工具。它以图形化的方式显示进程某个时间点的内存快照,我们可以从中分析到程序为什么会占用这么多内存,以及都是什么类型的内存。 内存类型 VMMap把内存分成下面几种类型: Image。这内存代表由image... Read More | Share it now!