|
|
Line 1: |
Line 1: |
| = Windows =
| |
|
| |
|
| Debugging under Windows is very straightforward using Microsoft Visual Studio 2005 (MSVC8).
| |
|
| |
| '''Note :''' Debugging E10s using MSVC9 should not be any different from MSVC8.
| |
|
| |
| === Creating a project ===
| |
|
| |
| First you need a 'Makefile' project. Open the "New Project" windows by hitting CTRL-SHIFT-N. Choose "Win32 Console Application" and type in whatever name and location you want for your new project. Hit the "OK" button.
| |
|
| |
| Then, open your project's properties by right-clicking the project name in the "Solution Explorer" view. Choose "Makefile" for the "Configuration Type" field in the "Configuration Properties / General" section. Click the "Apply" button now.
| |
|
| |
| In "General Properties / NMake" section, do the following :
| |
|
| |
| *type whatever make commands you want to use for building electrolysis, in the "general" subsection.
| |
| *type "OS_WIN;WIN32;XP_WIN;DEBUG;_DEBUG;MOZ_IPC" in the "Preprocessor Definitions" field of the "Intellisense" subsection.
| |
|
| |
| In "General Properties / Debugging" section, do the following :
| |
|
| |
| *type "<your_objdir>\browser\dist\bin\firefox.exe"
| |
| *type "-no-remote -P <your_electrolysis_profile_if_any> -chrome chrome://global/content/test-ipc.xul"
| |
| *type "MOZ_DEBUG_CHILD_PROCESS=1" in the "Environment" field
| |
|
| |
| === Debugging E10s ===
| |
|
| |
| First, make sure you've got a DEBUG build. If that's the case, you're all set ! Just hit F5 in MSVC. You should see the program's console with the main process output in it.
| |
|
| |
| If you have the MOZ_DEBUG_CHILD_PROCESS environment variable set, when the child process launches it should invoke the JIT debugger. (Note: to debug both processes in the same MSVC instance, you can attach to the child process from your MSVC instance and then hit Cancel.)
| |
|
| |
| You can instead set an environment variable MOZ_DEBUG_CHILD_PAUSE, which will pause the child process for 10 seconds when it starts, giving you time to attach:
| |
| *Hit CTRL-ALT-P to bring up the "attach to process" dialog
| |
| *Double-click "plugin-container.exe" in the "Available Processes" listbox.
| |
|
| |
| MSVC is going to load all the symbols from that newly attached process and, basically, you're done ! You should see two processes in your "processes" windows. You can break either one, or break all. You can switch from one to another by double-clicking on it, etc...
| |
|
| |
| And for a better debugging experience, don't forget to add breakpoints ! heh!
| |
|
| |
| = Linux =
| |
|
| |
| == To debug only the parent process ==
| |
|
| |
| With GDB: just pass "-g" to your regular command line, ex:
| |
|
| |
| $objdir/dist/bin/firefox -no-remote -P JunkProfile -g -chrome chrome://global/content/test-ipc.xul
| |
|
| |
| With emacs gud-gdb: run
| |
|
| |
| M-x gdb, gdb --annotate 3 --args $objdir/dist/bin/firefox-bin -no-remote -P JunkProfile -chrome chrome://global/content/test-ipc.xul
| |
| as you would for any other process.
| |
|
| |
| '''NOTE''': you'll need to make sure firefox's $objdir/dist/lib is in your LD_LIBRARY_PATH. Normally firefox -g would do this, but it doesn't play well with gud-gdb. Rather than add $objdir/dist/lib to your main LD_LIBRARY_PATH, you can set it only for gdb by adding this line to your ~/.gdbinit
| |
| set environment LD_LIBRARY_PATH=$objdir/dist/lib:/usr/lib/debug
| |
| (On Ubuntu, /usr/lib/debug contains libraries with better debug info if they're installed. If you don't have them installed or you use another another distro, you probably want /usr/lib instead.)
| |
|
| |
| '''WARNING''': if the first time you run firefox after a rebuild is from within gdb, gdb will crash when firefox restarts itself. You can stop firefox from restarting itself by setting the NO_EM_RESTART environment variable. Or you can just run firefox once from the shell, outside of gdb.
| |
|
| |
| '''WARNING''': if you send an interrupt to the terminal (^C), it will send a SIGINT to all processes attached to that terminal. gdb will catch this signal for the parent process and pause it, but, if there are no debuggers attached to child processes, then they will terminate. To only interrupt the parent process, either "kill -INT [PID]" in a separate shell, or start the application without the debugger and then attach a debugger from a separate terminal (as described for child processes below).
| |
|
| |
| == To debug child processes only ==
| |
|
| |
| Run firefox from the shell as
| |
| MOZ_DEBUG_CHILD_PROCESS=1 $objdir/dist/bin/firefox -no-remote -P JunkProfile -chrome chrome://global/content/test-ipc.xul
| |
| Each time a child process is launched, lines like this will be printed
| |
| CHILDCHILDCHILD
| |
| debug me @ [PID]
| |
|
| |
| ''(If you don't see this message, make sure you have set "dom.ipc.tabs.enabled" to "true", as described the [[Content Processes/Build|Build and Run Instructions]])''
| |
|
| |
| You can attach to the newly launched child process with "gdb attach [PID]", or, for emacs gud-gdb, with M-x gdb, gdb --annotate 3 attach PID.
| |
|
| |
| ''(If you see error message "ptrace: Operation not permitted", try set /proc/sys/kernel/yama/ptrace_scope or /etc/sysctl.d/10-ptrace.conf from 1 to 0 like the error suggests)''
| |
|
| |
| The same gdb session can be used to debug subsequent child processes.
| |
| However, even if the previous process has exited, an explicit "detach" is required before the next "attach [NEWPID]" to persuade gdb to reread the shared library mappings so that symbols have the right addresses.
| |
|
| |
| Note: the MOZ_DEBUG_CHILD_PAUSE environment variable added to distinguish between a pause and JIT debugging on Windows can also be used. It has the same effect as MOZ_DEBUG_CHILD_PROCESS.
| |
|
| |
| == To debug both the parent and children ==
| |
|
| |
| Make sure MOZ_DEBUG_CHILD_PROCESS is set in the environment you'll launch the parent from, and launch the parent process as above. Unfortunately, at the time of writing this, gud-gdb can't handle multiple gdb sessions. So to debug child processes, you'll need to pull up a new shell or a new emacs for each and attach to them as described for child processes above.
| |
|
| |
| == More Resources ==
| |
|
| |
| Don't forget to check out the [https://developer.mozilla.org/en/Debugging_Mozilla_with_gdb main Mozilla gdb guide].
| |
|
| |
| Also the [http://code.google.com/p/chromium/wiki/LinuxDebugging#GDB Google Chromium debugging guide for Linux] has some tips that also apply to our codebase, but we don't support all the tricks they show yet.
| |
|
| |
| '''TODO''' other debuggers?
| |
|
| |
| = IPDL =
| |
|
| |
| To see all IPC messages as they are sent, received, and processed set the environment variable
| |
| MOZ_IPC_MESSAGE_LOG=1
| |
| in the environment of the processes for which you wish to see messages.
| |
|
| |
| To see only IPC messages for a given toplevel protocol, set MOZ_IPC_MESSAGE_LOG to the name of the toplevel protocol. For example:
| |
|
| |
| MOZ_IPC_MESSAGE_LOG=PContent
| |
|
| |
| == Profiling ==
| |
|
| |
| To profile those messages, use the tool in {{bug|596725}}. You will get summarized output by message count and latency.
| |
|
| |
| == Working backwards from a C++ Message to its IPDL message ==
| |
|
| |
| The low-level C++ class used to transport IPDL messages around is <code>Message</code>. You'll see objects of this type in debuggers. Each source IPDL message declaration is assigned a unique ID. In C++, this can be read from <code>Message.type()</code>. Just before <code>Message</code>s are delivered to IPDL actors, they're also assigned a pretty name, accessible through <code>Message.name()</code>.
| |
|
| |
| Sometimes however, errors occur before <code>Message.name()</code> is assigned. For example, if a message is attempted to be dispatched to an actor that doesn't exist (probably because the actor was erroneously __delete__'d), then you'll see an error message like
| |
| ###!!! [Parent][RPCChannel] Error: Route error: message sent to unknown actor ID
| |
| and if you tried to read <code>Message.name()</code> in the parent process (in this example, where the dispatch failed), you would get back "???".
| |
|
| |
| It's possible in this case to work backwards from <code>Message.type()</code> to the IPDL message it represents. First you need to break the ID into its source protocol and the message enum within that protocol. You can do this by running
| |
| $ python $srcdir/ipc/ipdl/msgtype-components [MSGID]
| |
| Let's take the ID 3 as a random example. As of m-c 66096f867849, you would see
| |
| $ python $srcdir/ipc/ipdl/msgtype-components 3
| |
| protocol 0 message 3
| |
| Next we need to look up protocol 0. Open $objdir/ipc/ipdl/_ipdlheaders/IPCMessageStart.h. The protocol number is the enum value for IPCMessageStart (yes, the name is weird; long story). In this example, 0 is PAudioMsgStart, which is the PAudio protocol.
| |
| Next we need to look up the PAudio message. Open $objdir/ipc/ipdl/_ipdlheaders/mozilla/dom/PAudio.h. The message number is the '''ordinal''', not the actual value, for MessageType, starting from 0'th message. (This is because the protocol number is encoded in the message ID.) In this example, 3 (the third message) is Msg_SetVolume__ID.
| |
| So, in this example, <code>Message.type() = 3</code> refers to PAudio:SetVolume.
| |