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打开些别的起始页试试。

原创粉丝点击