firefox调试记录2——Firefox运行流程初步跟踪
2011-12-07 23:35
274 查看
前面对firefox打开火狐主页进行了跟踪,跟到了windows API 函数CreateProcess。该函数是创建一个新的进程,我认为要继续跟踪,可能需要进入这里创建的新进程才行。暂且搁下,我现在打算对firefox的运行流程进行一下粗粒度的跟踪,我的想法是一直按F10,直到程序主线程执行完毕,看看firefox在启动过程中和启动之后,程序都执行了哪些步骤。
首先将前面下的所有断点禁用,然后按第一次F10,程序开始执行,在nsWindowsWMain.cpp文件中wmain函数开始处。
接着按F10,程序执行经过如下代码段(我只贴出我在跟踪过程中实际执行了的代码,全部贴出来太多了):
= NULL;这一句。
继续按F10,来到代码段:
Firefox打开之后,可以在变量监视窗口看到,result的值为0,上面的代码之后的代码是:
这里再按F10就执行了exit(mainret);调试过程结束(firefox没有关闭的话还在运行着)。这一遍跟踪只搞清楚了一个问题,那就是firefox运行的时候启动的入口函数是wmain,这个函数就相当于普通C程序的main函数。下面是wmain函数的代码:
argvConverted);这一句。这个函数就是重点考察对象,在此,我还没得到我想要的过程,因此我选择继续跟进这个main函数。按照前面的方法,对这个main函数进行跟踪调试。
这个main函数是~firefox-8.0.source\browser\app\nsBrowserApp.cpp文件中的一个main函数,下面就不一步一步的描述了(这样的调试纯属体力活),我将重要的信息记录下来便是。main函数:
argc, argv);其参数exePath是在这个main函数中获取的,而argc和argv则是wmain函数传过来的。
继续跟进do_main函数。该函数内容如下:
这个函数在~\firefox-8.0.source\toolkit\xre\nsAppRunner.cpp文件里面,有一千多行(2612-3618),按F10实际执行了的代码:
的对象profileName和PRBool值startOffline其值为PR_FALSE。然后调用函数SelectProfile来让用户选择profile。其中第一个参数为getter_AddRefs(profileLock)的返回值,后面三个参数为之前定义的nativeApp,&startOffline和&profileName。该函数执行完毕,返回值rv= NS_ERROR_LAUNCHED_CHILD_PROCESS,于是XRE_main函数可以返回了。。
跟到这里,我认为SelectProfile函数在这样的运行条件下,如果用户选择正确的profile返回值必然是NS_ERROR_LAUNCHED_CHILD_PROCESS,如果取消profile的选择返回值必然是NS_ERROR_ABORT,因此我觉得这里没有必要再跟下去了,我对如何打开profilemanager的过程暂时不感兴趣。要搞清楚firefox是如何打开的必须跟到新打开的一个线程中区,前面已经跟踪到了那个CreateProcessW函数的入口处了。那么下一步就是进入新线程中进行跟踪了。
Wait,profilemanager打开用户选择profile之后,到创建新进程之间还有一些过程,我得去看看这些过程干了些什么。
这之间有一个函数LaunchChild(~\firefox-8.0.source\toolkit\xre\nsAppRunner.cpp文件中第1554-1634行),跟进去看看,该函数在我跟踪的时候执行了的代码为:
**argv)对比前面一个WinLaunchChild函数的参数,发现第三个参数类型不一样,一个为char **型,一个为PRUnichar **,上面只是将最后一个参数转换了一下类型,函数实际执行在后面的函数体里面,这两个函数的代码在~\firefox-8.0.source\toolkit\xre\nsWindowsRestart.cpp文件里面的第237-303行。
要搞清楚firefox是如何打开网页就得研究这个新创建的线程,不过到现在为止我感觉已经对firefox打开过程的粗粒度分析完毕,但是好像没有什么实际的收获,接下来先让firefox打开些别的起始页试试。
首先将前面下的所有断点禁用,然后按第一次F10,程序开始执行,在nsWindowsWMain.cpp文件中wmain函数开始处。
接着按F10,程序执行经过如下代码段(我只贴出我在跟踪过程中实际执行了的代码,全部贴出来太多了):
#ifndef XRE_DONT_PROTECT_DLL_LOAD mozilla::SanitizeEnvironmentVariables(); mozilla::NS_SetDllDirectory(L""); #endif接下来,执行了下面这几句:
char **argvConverted = new char*[argc + 1]; if (!argvConverted) return 127; for (int i = 0; i < argc; ++i) { argvConverted[i] = AllocConvertUTF16toUTF8(argv[i]); if (!argvConverted[i]) { return 127; } } argvConverted[argc] = NULL;这里先定义了**argvConverted,然后判断其是否为空,为空直接返回127(0xFF)否则对这个数组的每一个元素执行AllocConvertUTF16toUTF8(argv[i]);并判断是否为空。为空则返回127。按F10,单步执行下去,发现没有返回,而是直接执行到argvConverted[argc]
= NULL;这一句。
继续按F10,来到代码段:
// need to save argvConverted copy for later deletion. char **deleteUs = new char*[argc+1]; if (!deleteUs) { FreeAllocStrings(argc, argvConverted); return 127; } for (int i = 0; i < argc; i++) deleteUs[i] = argvConverted[i]; int result = main(argc, argvConverted);这段代码最后一句result = main(argc, argvConverted);是前面下第一个断点的地方,在这里程序调用一个main函数,打开了firefox的profilemanager,然后选择profile之后就打开了firefox,页面显示的是火狐主页。
Firefox打开之后,可以在变量监视窗口看到,result的值为0,上面的代码之后的代码是:
delete[] argvConverted; FreeAllocStrings(argc, deleteUs); return result;删除前面分配的内存并返回0.一直按F10,直到该函数(wmain)返回,该函数执行完毕,按F10时程序来到了D:\Program Files\Microsoft Visual Studio 10.0\VC\crt\src\crtexe.c文件,其中有代码(550-553行):
#ifdef WPRFLAG __winitenv = envp; mainret = wmain(argc, argv, envp); #else /* WPRFLAG */直接一个F10又到了下面的地方(565行):
if ( !managedapp ) exit(mainret);
这里再按F10就执行了exit(mainret);调试过程结束(firefox没有关闭的话还在运行着)。这一遍跟踪只搞清楚了一个问题,那就是firefox运行的时候启动的入口函数是wmain,这个函数就相当于普通C程序的main函数。下面是wmain函数的代码:
int wmain(int argc, WCHAR **argv)将这个wmain函数看一遍,发现没有执行多少有意义的事情,所有与打开firefox有关的看起来都在 int result = main(argc,
{
#ifndef XRE_DONT_PROTECT_DLL_LOAD mozilla::SanitizeEnvironmentVariables(); mozilla::NS_SetDllDirectory(L""); #endif
#ifdef XRE_WANT_DLL_BLOCKLIST
SetupDllBlocklist();
#endif
char **argvConverted = new char*[argc + 1];
if (!argvConverted)
return 127;
for (int i = 0; i < argc; ++i) {
argvConverted[i] = AllocConvertUTF16toUTF8(argv[i]);
if (!argvConverted[i]) {
return 127;
}
}
argvConverted[argc] = NULL;
// need to save argvConverted copy for later deletion.
char **deleteUs = new char*[argc+1];
if (!deleteUs) {
FreeAllocStrings(argc, argvConverted);
return 127;
}
for (int i = 0; i < argc; i++)
deleteUs[i] = argvConverted[i];
int result = main(argc, argvConverted);
delete[] argvConverted; FreeAllocStrings(argc, deleteUs); return result;
}
argvConverted);这一句。这个函数就是重点考察对象,在此,我还没得到我想要的过程,因此我选择继续跟进这个main函数。按照前面的方法,对这个main函数进行跟踪调试。
这个main函数是~firefox-8.0.source\browser\app\nsBrowserApp.cpp文件中的一个main函数,下面就不一步一步的描述了(这样的调试纯属体力活),我将重要的信息记录下来便是。main函数:
int main(int argc, char* argv[]) { char exePath[MAXPATHLEN]; nsresult rv = mozilla::BinaryPath::Get(argv[0], exePath); char *lastSlash = strrchr(exePath, XPCOM_FILE_PATH_SEPARATOR[0]); strcpy(++lastSlash, XPCOM_DLL); int gotCounters; IO_COUNTERS ioCounters; gotCounters = GetProcessIoCounters(GetCurrentProcess(), &ioCounters); rv = XPCOMGlueStartup(exePath); rv = XPCOMGlueLoadXULFunctions(kXULFuncs); #ifdef XRE_HAS_DLL_BLOCKLIST XRE_SetupDllBlocklist(); #endif if (gotCounters) { #if defined(XP_WIN) XRE_TelemetryAccumulate(mozilla::Telemetry::EARLY_GLUESTARTUP_READ_OPS, int(ioCounters.ReadOperationCount)); XRE_TelemetryAccumulate(mozilla::Telemetry::EARLY_GLUESTARTUP_READ_TRANSFER, int(ioCounters.ReadTransferCount / 1024)); IO_COUNTERS newIoCounters; int result; { ScopedLogging log; result = do_main(exePath, argc, argv); } XPCOMGlueShutdown(); return result; }这个函数获取了firefox.exe的路径,执行了XPCOMGlueStartup(exePath);和XPCOMGlueLoadXULFunctions(kXULFuncs);并运行了XRE_SetupDllBlocklist();最后调用函数do_main(exePath,
argc, argv);其参数exePath是在这个main函数中获取的,而argc和argv则是wmain函数传过来的。
继续跟进do_main函数。该函数内容如下:
static int do_main(const char *exePath, int argc, char* argv[]) { nsCOMPtr<nsILocalFile> appini; #ifdef XP_WIN nsresult rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(exePath), PR_FALSE, getter_AddRefs(appini)); appini->SetNativeLeafName(NS_LITERAL_CSTRING("application.ini")); const char *appDataFile = getenv("XUL_APP_FILE"); nsXREAppData *appData; rv = XRE_CreateAppData(appini, &appData); int result = XRE_main(argc, argv, appData); XRE_FreeAppData(appData); return result; }这个函数做的事情是根据前面传过来的exepath获得一个appini,并根据这个appini生成一个nsXREAppData 结构体appData,作为调用函数XRE_main(argc, argv, appData);的参数,其参数argc和argv依然是最开始那个。下面根进XRE_main。
这个函数在~\firefox-8.0.source\toolkit\xre\nsAppRunner.cpp文件里面,有一千多行(2612-3618),按F10实际执行了的代码:
Int XRE_main(int argc, char* argv[], const nsXREAppData* aAppData) { NS_TIME_FUNCTION; gXRE_mainTimestamp = PR_Now(); nsresult rv; ArgResult ar; #ifdef XP_WIN // Vista API. Mozilla is DPI Aware. typedef BOOL (*SetProcessDPIAwareFunc)(VOID); SetProcessDPIAwareFunc setDPIAware = (SetProcessDPIAwareFunc) GetProcAddress(LoadLibraryW(L"user32.dll"), "SetProcessDPIAware"); if (setDPIAware) setDPIAware(); #endif SetupErrorHandling(argv[0]); #ifdef CAIRO_HAS_DWRITE_FONT OSVERSIONINFO vinfo; vinfo.dwOSVersionInfoSize = sizeof(vinfo); #endif gArgc = argc; gArgv = argv; NS_ENSURE_TRUE(aAppData, 2); const char* override = nsnull; ar = CheckArg("override", PR_TRUE, &override); ScopedAppData appData(aAppData); gAppData = &appData; ScopedLogging log; if (!appData.xreDirectory) { nsCOMPtr<nsILocalFile> lf; rv = XRE_GetBinaryPath(gArgv[0], getter_AddRefs(lf)); nsCOMPtr<nsIFile> greDir; rv = lf->GetParent(getter_AddRefs(greDir)); rv = CallQueryInterface(greDir, &appData.xreDirectory); } nsXREDirProvider dirProvider; rv = dirProvider.Initialize(gAppData->directory, gAppData->xreDirectory); #ifdef MOZ_CRASHREPORTER if (EnvHasValue("MOZ_CRASHREPORTER")) { appData.flags |= NS_XRE_ENABLE_CRASH_REPORTER; } #endif SaveToEnv("MOZ_LAUNCHED_CHILD="); gRestartArgc = gArgc; gRestartArgv = (char**) malloc(sizeof(char*) * (gArgc + 1 + (override ? 2 : 0))); int i; for (i = 0; i < gArgc; ++i) { gRestartArgv[i] = gArgv[i]; } gRestartArgv[gRestartArgc] = nsnull; ar = CheckArg("safe-mode", PR_TRUE); ar = CheckArg("no-remote", PR_TRUE); rv = XRE_InitCommandLine(gArgc, gArgv); NS_ENSURE_SUCCESS(rv, 1); { ar = CheckArg("register", PR_TRUE); if (ar == ARG_BAD) { } else if (ar == ARG_FOUND) { } nsCOMPtr<nsINativeAppSupport> nativeApp; rv = NS_CreateNativeAppSupport(getter_AddRefs(nativeApp)); PRBool canRun = PR_FALSE; rv = nativeApp->Start(&canRun); #if defined(MOZ_UPDATER) && !defined(ANDROID) nsCOMPtr<nsIFile> updRoot; PRBool persistent; rv = dirProvider.GetFile(XRE_UPDATE_ROOT_DIR, &persistent, getter_AddRefs(updRoot)); ProcessUpdates(dirProvider.GetGREDir(), dirProvider.GetAppDir(), updRoot, gRestartArgc, gRestartArgv, appData.version); #endif nsCOMPtr<nsIProfileLock> profileLock; PRBool startOffline = PR_FALSE; nsCAutoString profileName; rv = SelectProfile(getter_AddRefs(profileLock), nativeApp, &startOffline, &profileName); if (rv == NS_ERROR_LAUNCHED_CHILD_PROCESS || rv == NS_ERROR_ABORT) return 0; }该函数对系统信息和版本信息等进行了进一步的获取,利用参数aAppData创建了ScopedAppData 类的对象 appData,两个nsCOMPtr结构profileLock和nativeApp。定义了类nsCAutoString
的对象profileName和PRBool值startOffline其值为PR_FALSE。然后调用函数SelectProfile来让用户选择profile。其中第一个参数为getter_AddRefs(profileLock)的返回值,后面三个参数为之前定义的nativeApp,&startOffline和&profileName。该函数执行完毕,返回值rv= NS_ERROR_LAUNCHED_CHILD_PROCESS,于是XRE_main函数可以返回了。。
跟到这里,我认为SelectProfile函数在这样的运行条件下,如果用户选择正确的profile返回值必然是NS_ERROR_LAUNCHED_CHILD_PROCESS,如果取消profile的选择返回值必然是NS_ERROR_ABORT,因此我觉得这里没有必要再跟下去了,我对如何打开profilemanager的过程暂时不感兴趣。要搞清楚firefox是如何打开的必须跟到新打开的一个线程中区,前面已经跟踪到了那个CreateProcessW函数的入口处了。那么下一步就是进入新线程中进行跟踪了。
Wait,profilemanager打开用户选择profile之后,到创建新进程之间还有一些过程,我得去看看这些过程干了些什么。
这之间有一个函数LaunchChild(~\firefox-8.0.source\toolkit\xre\nsAppRunner.cpp文件中第1554-1634行),跟进去看看,该函数在我跟踪的时候执行了的代码为:
static nsresult LaunchChild(nsINativeAppSupport* aNative, PRBool aBlankCommandLine = PR_FALSE) { aNative->Quit(); // release DDE mutex, if we're holding it SaveToEnv("MOZ_LAUNCHED_CHILD=1"); #if defined(ANDROID) #else #if defined(XP_MACOSX) #else nsCOMPtr<nsILocalFile> lf; nsresult rv = XRE_GetBinaryPath(gArgv[0], getter_AddRefs(lf)); if (NS_FAILED(rv)) return rv; #if defined(XP_WIN) nsAutoString exePath; rv = lf->GetPath(exePath); if (!WinLaunchChild(exePath.get(), gRestartArgc, gRestartArgv)) return NS_ERROR_FAILURE; return NS_ERROR_LAUNCHED_CHILD_PROCESS; }直接看WinLaunchChild函数,关键代码
WinLaunchChild(const PRUnichar *exePath, int argc, char **argv) { … for (int i = 0; i < argc; ++i) { argvConverted[i] = AllocConvertUTF8toUTF16(argv[i]); if (!argvConverted[i]) { return FALSE; } } BOOL ok = WinLaunchChild(exePath, argc, argvConverted); … }最后又调用BOOL ok = WinLaunchChild(exePath, argc, argvConverted);就在这段代码的后面,BOOL WinLaunchChild(const PRUnichar *exePath, int argc, PRUnichar
**argv)对比前面一个WinLaunchChild函数的参数,发现第三个参数类型不一样,一个为char **型,一个为PRUnichar **,上面只是将最后一个参数转换了一下类型,函数实际执行在后面的函数体里面,这两个函数的代码在~\firefox-8.0.source\toolkit\xre\nsWindowsRestart.cpp文件里面的第237-303行。
BOOL WinLaunchChild(const PRUnichar *exePath, int argc, PRUnichar **argv) { PRUnichar *cl; BOOL ok; cl = MakeCommandLine(argc, argv); STARTUPINFOW si = {sizeof(si), 0}; PROCESS_INFORMATION pi = {0}; ok = CreateProcessW(exePath, cl, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); if (ok) { CloseHandle(pi.hProcess); CloseHandle(pi.hThread); } else { } free(cl); return ok; }除了exePath传给了CreateProcessW函数,其他参数都用在了MakeCommandLine函数里,其返回值cl作为CreateProcessW的一个参数。另外,还定义了结构体si、pi。
要搞清楚firefox是如何打开网页就得研究这个新创建的线程,不过到现在为止我感觉已经对firefox打开过程的粗粒度分析完毕,但是好像没有什么实际的收获,接下来先让firefox打开些别的起始页试试。
相关文章推荐
- firefox调试记录5——事先确定profile情况下的跟踪
- firefox调试记录3——其他情况的跟踪
- firefox调试记录1——用VS2010调试跟踪firefox8
- LTE系统调试记录6:TMS320C66x程序运行时间测量方法
- 跟踪源码运行流程---SpringMVC学习笔记(四)
- 高通Secure Boot调试流程记录
- firefox调试记录6——Firefox主窗口创建过程研究
- firefox调试记录8——小结
- NPAPI插件开发详细记录:插件运行流程分析
- firefox调试记录7——打开与修改文件
- gdb调试运行程序带参数(调用动态链接库),debug过程记录
- 跟踪源码运行流程---SpringMVC学习笔记(四)
- firefox调试记录4——关于跨进程的调试
- 魔兽私服TrinityCore 运行调试流程
- QT学习记录(1)环境配置和初步建立项目运行
- 运维调试记录:Opendaylight铍版本开发环境搭建流程
- 《ReactNative入坑记录(二)ReactNative项目的创建、运行及调试(Android为例)》
- net-snmp(5.6.1) 编译 安装 运行调试全记录
- PX4固定翼调试校准流程及实验相关问题记录分析
- 第二次调试 OpenIPMP并初步成功的记录