前面对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;这一句。
// 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,页面显示的是火狐主页。
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);
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函数进行跟踪调试。
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函数传过来的。
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。
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函数可以返回了。。
这之间有一个函数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。