[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Minios-devel] [UNIKRAFT PATCH v2 5/8] support/scripts: fetch trace data from running Unikraft
Fetch tracebuffer, key-values and tracepoint definitions using objcopy and gdb. This patch introduces uk-gdb.py script, which is a gdb-extension providing handy helpers. Currently, it is only tracepoints related stuff, but possibilities are going beyond that. The uk-gdb.py can be loaded into gdb manually (via "source" command) or using gdb's auto-load feature. Gdb looks for a file named objfile-gdb.py, where objfile is the object file's real name. To achieve that uk-gdb.py is copied into the build directory with a little modification - hardcode the absolute path to the Unikraft's scripts directory. The trace.py is a tool for analyzing traces. Currently, it does only the basic stuff: fetches tracing data from a running instance (can run gdb for you) and prints out parsed information (in the next patch). But it will eventually be able to do more. For example statistics of most frequently occurred events. Signed-off-by: Yuri Volchkov <yuri.volchkov@xxxxxxxxx> --- Makefile | 12 ++- support/scripts/uk-gdb.py | 122 +++++++++++++++++++++++++++ support/scripts/uk_trace/__init__.py | 0 support/scripts/uk_trace/parse.py | 57 +++++++++++++ support/scripts/uk_trace/trace.py | 103 ++++++++++++++++++++++ 5 files changed, 293 insertions(+), 1 deletion(-) create mode 100644 support/scripts/uk-gdb.py create mode 100644 support/scripts/uk_trace/__init__.py create mode 100644 support/scripts/uk_trace/parse.py create mode 100755 support/scripts/uk_trace/trace.py diff --git a/Makefile b/Makefile index c0492920..e423be8d 100644 --- a/Makefile +++ b/Makefile @@ -534,8 +534,18 @@ libs: $(UK_ALIBS) $(UK_ALIBS-y) $(UK_OLIBS) $(UK_OLIBS-y) images: $(UK_IMAGES) $(UK_IMAGES-y) -all: images +GDB_HELPER_LINKS := $(addsuffix .dbg-gdb.py,$(UK_IMAGES-y) $(UK_IMAGES)) +$(GDB_HELPER_LINKS): + $(call verbose_cmd,LN,$(notdir $@), ln -sf uk-gdb.py $@) +SCRIPTS_DIR_BACKSLASHED = $(subst /,\/,$(SCRIPTS_DIR)) +$(BUILD_DIR)/uk-gdb.py: $(SCRIPTS_DIR)/uk-gdb.py + $(call verbose_cmd,GEN,$(notdir $@), \ + sed '/scripts_dir = / s/os.path.dirname(os.path.realpath(__file__))/"$(SCRIPTS_DIR_BACKSLASHED)"/g' $^ > $@) + +gdb_helpers: $(GDB_HELPER_LINKS) $(BUILD_DIR)/uk-gdb.py + +all: images gdb_helpers ################################################################################ # Cleanup rules ################################################################################ diff --git a/support/scripts/uk-gdb.py b/support/scripts/uk-gdb.py new file mode 100644 index 00000000..e4b0038d --- /dev/null +++ b/support/scripts/uk-gdb.py @@ -0,0 +1,122 @@ +#!/user/bin/env python3 +# SPDX-License-Identifier: BSD-3-Clause */ +# +# Authors: Yuri Volchkov <yuri.volchkov@xxxxxxxxx> +# +# Copyright (c) 2019, NEC Laboratories Europe GmbH, NEC Corporation. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. + +import gdb +import pickle +import os, sys +import tempfile, shutil + +scripts_dir = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(scripts_dir) + +import uk_trace.parse as parse + +type_char = gdb.lookup_type('char') +type_void = gdb.lookup_type('void') + +PTR_SIZE = type_void.pointer().sizeof + +def get_trace_buffer(): + inf = gdb.selected_inferior() + + try: + trace_buff = gdb.parse_and_eval('uk_trace_buffer') + trace_buff_size = trace_buff.type.sizeof + trace_buff_addr = int(trace_buff.address) + trace_buff_writep = int(gdb.parse_and_eval('uk_trace_buffer_writep')) + except gdb.error: + gdb.write("Error getting the trace buffer. Is tracing enabled?\n") + raise gdb.error + + if (trace_buff_writep == 0): + # This can happen as effect of compile optimization if none of + # tracepoints were called + used = 0 + else: + used = trace_buff_writep - trace_buff_addr + + return bytes(inf.read_memory(trace_buff_addr, used)) + +def save_traces(out): + elf = gdb.current_progspace().filename + + pickler = pickle.Pickler(out) + + # keyvals need to go first, because they have format_version + # key. Even if the format is changed we must guarantee that at + # least keyvals are always stored first. However, ideally, next + # versions should just have modifications at the very end to keep + # compatibility with previously collected data. + pickler.dump(parse.get_keyvals(elf)) + pickler.dump(elf) + pickler.dump(PTR_SIZE) + # We are saving raw trace buffer here. Another option is to pickle + # already parsed samples. But in the chosen case it is a lot + # easier to debug the parser, because python in gdb is not very + # convenient for development. + pickler.dump(parse.get_tp_sections(elf)) + pickler.dump(get_trace_buffer()) + +class uk(gdb.Command): + def __init__(self): + gdb.Command.__init__(self, 'uk', + gdb.COMMAND_USER, gdb.COMPLETE_COMMAND, True) + +class uk_trace(gdb.Command): + def __init__(self): + gdb.Command.__init__(self, 'uk trace', + gdb.COMMAND_USER, gdb.COMPLETE_COMMAND, True) + def invoke(self, arg, from_tty): + # TODO + pass + +class uk_trace_save(gdb.Command): + def __init__(self): + gdb.Command.__init__(self, 'uk trace save', + gdb.COMMAND_USER, gdb.COMPLETE_COMMAND) + def invoke(self, arg, from_tty): + if not arg: + gdb.write('Missing argument. Usage: uk trace save <filename>\n') + return + + gdb.write('Saving traces to %s ...\n' % arg) + + with tempfile.NamedTemporaryFile() as out: + save_traces(out) + out.flush() + shutil.copyfile(out.name, arg) + +uk() +uk_trace() +uk_trace_save() diff --git a/support/scripts/uk_trace/__init__.py b/support/scripts/uk_trace/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/support/scripts/uk_trace/parse.py b/support/scripts/uk_trace/parse.py new file mode 100644 index 00000000..407f47e3 --- /dev/null +++ b/support/scripts/uk_trace/parse.py @@ -0,0 +1,57 @@ +# SPDX-License-Identifier: BSD-3-Clause */ +# +# Authors: Yuri Volchkov <yuri.volchkov@xxxxxxxxx> +# +# Copyright (c) 2019, NEC Laboratories Europe GmbH, NEC Corporation. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. + +import subprocess +import re +import tempfile + +def get_tp_sections(elf): + f = tempfile.NamedTemporaryFile() + objcopy_cmd = 'objcopy -O binary %s ' % elf + objcopy_cmd += '--only-section=.uk_tracepoints_list ' + f.name + objcopy_cmd = objcopy_cmd.split() + subprocess.check_call(objcopy_cmd) + return f.read() + +def get_keyvals(elf): + readelf_cmd = 'readelf -p .uk_trace_keyvals %s' % elf + readelf_cmd = readelf_cmd.split() + raw_data = subprocess.check_output(readelf_cmd).decode() + filtered = re.findall(r'^\s*\[ *\d+\]\s+(.+) = (.+)$', raw_data, + re.MULTILINE) + + ret = dict() + for key,val in filtered: + ret[key] = val + + return ret diff --git a/support/scripts/uk_trace/trace.py b/support/scripts/uk_trace/trace.py new file mode 100755 index 00000000..10c985e5 --- /dev/null +++ b/support/scripts/uk_trace/trace.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: BSD-3-Clause */ +# +# Authors: Yuri Volchkov <yuri.volchkov@xxxxxxxxx> +# +# Copyright (c) 2019, NEC Laboratories Europe GmbH, NEC Corporation. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. + +import click +import os, sys +import pickle +import subprocess + +import parse + +@click.group() +def cli(): + pass + +def parse_tf(trace_file): + try: + with open(trace_file, 'rb') as tf: + unpickler = pickle.Unpickler(tf) + + keyvals = unpickler.load() + elf = unpickler.load() + ptr_size = unpickler.load() + tp_defs = unpickler.load() + trace_buff = unpickler.load() + except EOFError: + print("Unexpected end of trace file", file=sys.stderr) + quit(-1) + except Exception as inst: + print("Problem occurred during reading the tracefile: %s" % str(inst)) + quit(-1) + + return parse.sample_parser(keyvals, tp_defs, trace_buff, ptr_size) + +@cli.command() +@click.argument('uk_img', type=click.Path(exists=True)) +@click.option('--out', '-o', type=click.Path(), + default='tracefile', show_default=True, + help='Output binary file') +@click.option('--remote', '-r', type=click.STRING, + default=':1234', show_default=True, + help='How to connect to the gdb session '+ + '(parameters for "target remote" command)') +@click.option('--verbose', is_flag=True, default=False) +def fetch(uk_img, out, remote, verbose): + """Fetch binary trace file from Unikraft (using gdb)""" + + if os.path.exists(out): + os.remove(out) + + helper_path = os.path.abspath(uk_img) + '-gdb.py' + gdb_cmd = ['gdb', '-nh', '-batch', + click.format_filename(uk_img), + '-iex', 'add-auto-load-safe-path ' + helper_path, + '-ex', 'target remote ' + remote, + '-ex', 'uk trace save ' + out + ] + + proc = subprocess.Popen(gdb_cmd, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT + ) + _stdout, _ = proc.communicate() + _stdout = _stdout.decode() + if proc.returncode or not os.path.exists(out): + print(_stdout) + sys.exit(1) + if verbose: + print(_stdout) + + +if __name__ == '__main__': + cli() -- 2.19.2 _______________________________________________ Minios-devel mailing list Minios-devel@xxxxxxxxxxxxxxxxxxxx https://lists.xenproject.org/mailman/listinfo/minios-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |