firefox调试记录2——Firefox运行流程初步跟踪
来源:互联网 发布:ios理财软件 编辑:程序博客网 时间:2024/06/11 19:57
前面对firefox打开火狐主页进行了跟踪,跟到了windows API 函数CreateProcess。该函数是创建一个新的进程,我认为要继续跟踪,可能需要进入这里创建的新进程才行。暂且搁下,我现在打算对firefox的运行流程进行一下粗粒度的跟踪,我的想法是一直按F10,直到程序主线程执行完毕,看看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){#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;}将这个wmain函数看一遍,发现没有执行多少有意义的事情,所有与打开firefox有关的看起来都在 int result = main(argc, 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调试记录2——Firefox运行流程初步跟踪
- firefox调试记录3——其他情况的跟踪
- firefox调试记录1——用VS2010调试跟踪firefox8
- firefox调试记录5——事先确定profile情况下的跟踪
- firefox调试记录8——小结
- firefox调试记录6——Firefox主窗口创建过程研究
- firefox调试记录7——打开与修改文件
- Firefox初步
- firefox调试记录4——关于跨进程的调试
- firefox调试
- JavaScript调试工具——Firefox中的插件Firebug介绍
- firefox
- Firefox
- FireFox
- firefox
- FireFox
- firefox
- firefox
- 学习QT的一些相关博客和自己的一点总结
- secureCRT进入sqlplus能上下查看历史sql语句以及左右修改字符
- 初学qt的痛苦体会
- VI 实用心得体会
- User Story 在敏捷开发过程中的应用
- firefox调试记录2——Firefox运行流程初步跟踪
- Fixwin电子窗体——基本信息模板
- android布局之相对布局(RelativeLayout)
- 读《道德经》第一章
- 网站测试流程、要求及测试报告
- 我的emacs配置文件
- OpenSessionInViewFilter 说明与配置
- 我的screenrc
- 使用 JAVA 将数字金额转换成大写中文金额