![]() |
|||
|
|
|
|
|
|||
|
Advanced UsageLeaner Executables: External Debug SymbolsSuppose that you are building a C or C++ Linux program that is going to be installed on tens or hundreds of your production machines. Since this software is not shipped to customers, you may as well leave the debug information in, to help you later with troubleshooting. For complex programs the size of the debug information (especially for C++ programs where symbol names often get mangled into very long strings) may be considerable, and it may impact your deployment time. Hopefully you will not need the debug symbols as often. What if you could store the debug information on only one server instead of N? Turns out you can pull this trick easily with the following bash script (which you can include in your Makefile as a post-build step):
#! /bin/bash
DBGFILE=DebugInfoServerNetworkMountedPath/$1.dbg
if objcopy --only-keep-debug $1 $DBGFILE; then
#strip -d $1 # strip debug info, or strip everything:
strip $1
objcopy --add-gnu-debuglink=$DBGFILE $1
fi
And that's it. "But how is the debugger going to know how to locate the debug information, since we stripped it out?" one may ask. Simple. The Custom Data VisualizationLet us consider the following (however contrived) example:
#include <iostream>
#include <string>
enum AccessLevel
{
ACCESS_NONE,
ACCESS_GUEST,
ACCESS_NORMAL,
ACCESS_ADMIN
};
class User
{
std::string name_;
AccessLevel access_;
public:
explicit User(const char* name, AccessLevel a = ACCESS_NORMAL)
: access_(a)
{
if (name) name_.assign(name);
}
void print(std::ostream& out) const
{
out << "name=" << name_ << ", access=" << access_;
}
};
inline std::ostream&
operator<<(std::ostream& out, const User& user)
{
user.print(out);
return out;
}
int main()
{
User jack("John Doe");
User admin("Kahuna", ACCESS_ADMIN);
std::cout << jack << std::endl;
std::cout << admin << std::endl;
return 0;
}
When this code is compiled and executed under the debugger,
the variables "jack" and "admin" can be visualized in the
Local Variables tab: The name_ member variable (which is of the std::string type) is being shown by the debugger as if it were a good old C-style string. The magic behind this convenient feature is implemented in a piece of Python code in the file /usr/local/zero/plugin/.zero.py.
The debugger looks for a Python function conventionaly named
The Users can go further and extend and override the default implementation, for example:
import zero
def do_nothing(sym):
pass
if __name__ == "__main__":
try:
oldfun = on_debug_symbol
except:
oldfun = do_nothing
def on_debug_symbol(sym):
result = oldfun(sym)
if not result:
if sym.type().name() == "User":
mem = sym.children()
name = oldfun(mem[0]) #transform std::string
if not name:
name = mem[0]
tip = name.value() + ", " + mem[1].value()
sym.set_tooltip(tip)
return result
Assuming that the python code shown above lives in a file named user.py, you may invoke it by passing the --py-run=user.py to the debugger command line. Another alternative is to modify the code in /usr/local/zero/plugin/.zero.py based on the example above. If you chose to make changes to the .zero.py script, then you can arrange for your custom data visualizations to be enabled and disabled from the graphical user interface. In the Language tab of the Tools, Options dialog, note the second group of check buttons:
If you peek at the .zero.py script you will notice this code at the beginning of the file:
The get_configurable_params functions is the internal
protocol between the script and the main debugger program. Try adding
this line to the param dictionary:
Restart the debugger, then open the Tools, Options dialog, and select the Language tab. Notice the new check button? Now add this code right at the top of your user-defined function:
so that any custom data transformation is skipped when the check box is turned off.
|
|
Home | Top | Up | Community |
||