您的位置:首页 > 其它

firefox调试记录2——Firefox运行流程初步跟踪

2011-12-07 23:35 274 查看
前面对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打开些别的起始页试试。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息