Hello, Genode
I'm trying to understand how env() and env()->parent() work from the source code. First, I find the body of env function located at: genode/base/src/base/env/env.cc
and looks like this: namespace Genode {
/* * Request pointer to static environment of the Genode application */ Env *env() { /* * By placing the environment as static object here, we ensure that its * constructor gets called when this function is used the first time. */ static Genode::Platform_env _env;
return &_env; } }
env() function looks like, at namespace Genode, it returns the pointer of singleton object _env (static Genode::Platform_env _env). But, when I call env() at different places, it returns different addresses.
For instance, - at genode/base/src/core/main.cc - at dde_linux/src/drivers/usb/nic/nic.cc, env() function returns different addresses like the following - at genode/base/src/core/main.cc, it returns 801f01a0 - at dde_linux/src/drivers/usb/nic/nic.cc, it returns 10b716c.
For me, it is very strange, because both functions returns the address of a singleton object _env. I think I miss some points and appreciate if you fill the gap.
And, one more thing: I was trying to print some debug message at Env *env() function like the following.
Env *env() { /* * By placing the environment as static object here, we ensure that its * constructor gets called when this function is used the first time. */ static Genode::Platform_env _env; PDBG("Genode::env() function called"); // my debug message
return &_env; }
And, when I run the genode, it always halt at core and child init is not running. Do you have any clues?
Thanks always, Jaeyong
Hello Jaeyong,
your observations are actually plausible. Let me shed some light on both of them.
I'm trying to understand how env() and env()->parent() work from the source code. First, I find the body of env function located at: genode/base/src/base/env/env.cc
and looks like this: namespace Genode { Env *env() { static Genode::Platform_env _env; return &_env; } }
env() function looks like, at namespace Genode, it returns the pointer of singleton object _env (static Genode::Platform_env _env). But, when I call env() at different places, it returns different addresses.
For instance,
- at genode/base/src/core/main.cc
- at dde_linux/src/drivers/usb/nic/nic.cc,
env() function returns different addresses like the following
- at genode/base/src/core/main.cc, it returns 801f01a0
- at dde_linux/src/drivers/usb/nic/nic.cc, it returns 10b716c.
The 'env()' function is part of the env library, which is linked to each Genode program. For each executable, the linker decides where in the virtual address space of the program the symbols of the library go. In your case, you are looking at two different programs (core and usb_drv). The address space layout of both programs is different. That includes the layout of the respective BSS segments. For this reason, each program returns a different address. When calling 'env()' from different portions of the same program, the returned addresses will be the same.
And, one more thing: I was trying to print some debug message at Env *env() function like the following.
Env *env() { static Genode::Platform_env _env; PDBG("Genode::env() function called"); // my debug message return &_env; }
And, when I run the genode, it always halt at core and child init is not running. Do you have any clues?
The system does actually not halt at core but right at the start of the init process. By inserting the 'PDBG' statement, you just created a cyclic dependency within init. The 'PDBG' function uses the core's LOG service to print the message. Hence, init needs to create a LOG session, which is done by calling 'Genode::env()->parent()->session(...)'. Hence, when 'env()' is called the first time (apparently when init tries to obtain its config), the function tries to indirectly call 'env()'. To illustrate the situation, the backtrace of init looks as follows:
main init/main.cc:135 Genode::config os/config.h:90 Genode::Config::Config os/config.h:46 Rom_connection rom_session/connection.h:55 _create_session rom_session/connection.h:36 Genode::Connection::session base/connection.h:93 Genode::env env.cc:31 Genode::printf log_console.cc:141 Genode::vprintf log_console.cc:149 stdout_log_console log_console.cc:115 Log_console log_console.cc:68 Log_connection log_session/connection.h:27 Genode::Connection::session base/connection.h:93 Genode::env env.cc:31 Genode::printf log_console.cc:141 Genode::vprintf log_console.cc:149 stdout_log_console log_console.cc:115
At the second recursion level, the static constructor of 'Platform_env' is already in locked state ('__cxa_guard_acquire' was called at the first call of 'env()'). Hence, the programs tries to obtain the same lock twice and deadlocks.
Cheers Norman
Thanks a lot Norman. It helped a lot understand how env works.
One more question is that "how do you backtrace init?" And, by any chance, is there any builtin function that performs backtrace (like backtrace function of glibc in Linux)?
Best, Jaeyong
On Sun, Nov 18, 2012 at 3:35 AM, Norman Feske <norman.feske@...1...>wrote:
Hello Jaeyong,
your observations are actually plausible. Let me shed some light on both of them.
I'm trying to understand how env() and env()->parent() work from the
source
code. First, I find the body of env function located at: genode/base/src/base/env/env.cc
and looks like this: namespace Genode { Env *env() { static Genode::Platform_env _env; return &_env; } }
env() function looks like, at namespace Genode, it returns the pointer of singleton object _env (static Genode::Platform_env _env). But, when I call env() at different places, it returns different
addresses.
For instance,
- at genode/base/src/core/main.cc
- at dde_linux/src/drivers/usb/nic/nic.cc,
env() function returns different addresses like the following
- at genode/base/src/core/main.cc, it returns 801f01a0
- at dde_linux/src/drivers/usb/nic/nic.cc, it returns 10b716c.
The 'env()' function is part of the env library, which is linked to each Genode program. For each executable, the linker decides where in the virtual address space of the program the symbols of the library go. In your case, you are looking at two different programs (core and usb_drv). The address space layout of both programs is different. That includes the layout of the respective BSS segments. For this reason, each program returns a different address. When calling 'env()' from different portions of the same program, the returned addresses will be the same.
And, one more thing: I was trying to print some debug message at Env
*env()
function like the following.
Env *env() { static Genode::Platform_env _env; PDBG("Genode::env() function called"); // my debug message return &_env; }
And, when I run the genode, it always halt at core and child init is not running. Do you have any clues?
The system does actually not halt at core but right at the start of the init process. By inserting the 'PDBG' statement, you just created a cyclic dependency within init. The 'PDBG' function uses the core's LOG service to print the message. Hence, init needs to create a LOG session, which is done by calling 'Genode::env()->parent()->session(...)'. Hence, when 'env()' is called the first time (apparently when init tries to obtain its config), the function tries to indirectly call 'env()'. To illustrate the situation, the backtrace of init looks as follows:
main init/main.cc:135 Genode::config os/config.h:90 Genode::Config::Config os/config.h:46 Rom_connection rom_session/connection.h:55 _create_session rom_session/connection.h:36 Genode::Connection::session base/connection.h:93 Genode::env env.cc:31 Genode::printf log_console.cc:141 Genode::vprintf log_console.cc:149 stdout_log_console log_console.cc:115 Log_console log_console.cc:68 Log_connection log_session/connection.h:27 Genode::Connection::session base/connection.h:93 Genode::env env.cc:31 Genode::printf log_console.cc:141 Genode::vprintf log_console.cc:149 stdout_log_console log_console.cc:115
At the second recursion level, the static constructor of 'Platform_env' is already in locked state ('__cxa_guard_acquire' was called at the first call of 'env()'). Hence, the programs tries to obtain the same lock twice and deadlocks.
Cheers Norman
-- Dr.-Ing. Norman Feske Genode Labs
http://www.genode-labs.com · http://genode.org
Genode Labs GmbH · Amtsgericht Dresden · HRB 28424 · Sitz Dresden Geschäftsführer: Dr.-Ing. Norman Feske, Christian Helmuth
Monitor your physical, virtual and cloud infrastructure from a single web console. Get in-depth insight into apps, servers, databases, vmware, SAP, cloud infrastructure, etc. Download 30-day Free Trial. Pricing starts from $795 for 25 servers or applications! http://p.sf.net/sfu/zoho_dev2dev_nov _______________________________________________ Genode-main mailing list Genode-main@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/genode-main
Hello Jaeyong,
One more question is that "how do you backtrace init?"
this depends on the used base platform. In this particular case, I reproduced the issue using the 'base-linux' platform where each Genode process is a plain Linux process. Hence, it is possible to attach the GNU debugger to the individual process:
gdb -p `pidof "[Genode] init"`
The 'bt' GDB command reveals the backtrace. Pretty convenient, isn't it?
On L4/Fiasco and Fiasco.OC, the kernel debugger comes with a basic backtrace feature. After breaking-in into the debugger (by pressing [esc]), you can list all threads using the 'lp' command. There you can see all threads and their respective IDs. This ID can then be specified to the backtrace command ('btt'). The back trace is just a bunch of EIP values, which can be looked up in the disassembled binary. (e.g., searching in the output of 'objdump -lSd') Alternatively, you might use a convenience script that comes with the Fiasco kernel and parses the EIP values for you. It is located at 'base-foc/contrib/kernel/fiasco/tool/backtrace' (after you issued 'make prepare' within the 'base-foc' directory).
On other kernels such as OKL4 and L4ka::Pistachio, obtaining backtraces must be done by hand by dumping the user stack of the corresponding thread and looking for "interesting" addresses. This is less convenient but principally works. On NOVA or base-hw, no kernel debugger is available. Here, the "-S" option of qemu becomes handy, which allows GDB to be attached to Qemu.
In practice, we often find ourselves jumping from one kernel to another while debugging because the facilities are so different and most code on Genode (and thereby also the bugs) is platform-agnostic anyway. ;-)
And, by any chance, is there any builtin function that performs backtrace (like backtrace function of glibc in Linux)?
There is the built-in compiler function command '__builtin_return_address'. On the x86 architecture, you can obtain the return address of the current stack frame as follows:
PLOG("came from: %p", __builtin_return_address(0));
Using the argument, it is possible to select the stack frame, in which you are interested in. Unfortunately, this capability is severe limited on ARM.
I hope these pointers will be of help. If you are interested in reading about further user-level debugging options for Genode, I recommend the following document:
http://genode.org/documentation/developer-resources/gdb
Cheers Norman