Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for gdb-oneapi #43

Merged
merged 2 commits into from
Jul 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion scripts/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ dist_bin_SCRIPTS = run_one_sec
bin_SCRIPTS = STAT stat-cl stat-script stat-core-merger attach-helper
python_PYTHON = STATmain.py stat_merge_base.py STATmerge.py core_file_merger.py bg_core_backtrace.py attach_helper.py STAThelper.py
if ENABLE_GDB
python_PYTHON += stat_cuda_gdb.py cuda_gdb.py gdb.py roc_gdb.py
python_PYTHON += stat_cuda_gdb.py cuda_gdb.py gdb.py roc_gdb.py oneapi_gdb.py
endif
if ENABLE_GUI
python_PYTHON += STATGUI.py STATview.py
Expand Down
40 changes: 37 additions & 3 deletions scripts/core_file_merger.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
interrupt
"""

__copyright__ = """Copyright (c) 2007-2020, Lawrence Livermore National Security, LLC."""
__copyright__ = """Copyright (c) 2007-2022, Lawrence Livermore National Security, LLC."""
__license__ = """Produced at the Lawrence Livermore National Laboratory
Written by Dane Gardner, Gregory Lee <lee218@llnl.gov>, Dorian Arnold, Matthew LeGendre, Dong Ahn, Bronis de Supinski, Barton Miller, Martin Schulz, Niklas Nielson, Nicklas Bo Jensen, Jesper Nielson, and Sven Karlsson.
LLNL-CODE-750488.
Expand Down Expand Up @@ -144,6 +144,7 @@ def communicate(self, command):
if not command.endswith('\n'):
command += '\n'
self.subprocess.stdin.write(command.encode('utf-8'))
self.subprocess.stdin.flush()
return self.readlines()


Expand All @@ -168,6 +169,36 @@ def open(self, corefile, executable = None):
self.subprocess = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
return self.readlines()

class GdbOneapi(Gdb):

def __init__ (self, options):
Gdb.__init__(self, options)
self.executable = "GPU"

def open(self, corefile, executable = None):
args = []
args.append('gdb-oneapi')
args.append('-ex')
args.append("set pagination 0")
args.append('-ex')
args.append("cd %s" %(self.directory))
args.append('-ex')
args.append("path %s" %(self.objectpath))
args.append('-ex')
args.append("directory %s" %(self.sourcepath))
args.append('-ex')
args.append("set filename-display absolute")
args.append('-ex')
target_string = "target core "
self.corefile = corefile
if os.path.isabs(self.corefile):
target_string += " %s" % (self.corefile)
else:
target_string += " %s/%s" % (self.coredir, self.corefile)
args.append(target_string)
self.subprocess = subprocess.Popen(args, universal_newlines=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
return self.readlines()


###############################################################################
class CoreFile:
Expand Down Expand Up @@ -352,6 +383,8 @@ def process_core(self):
logging.info("Connecting gdb to the core file (%s)"%self.coreData['coreFile'])
if CoreFile.__options['cuda'] == 1:
gdb = CudaGdb(CoreFile.__options)
elif CoreFile.__options['oneapi'] == 1:
gdb = GdbOneapi(CoreFile.__options)
else:
gdb = Gdb(CoreFile.__options)
executable = gdb.executable
Expand Down Expand Up @@ -467,7 +500,7 @@ def process_core(self):
in_thread,functions = True,[]

#In some cases, gdb will quit the stack trace early
elif 'Backtrace stopped: ' in line:
elif 'Backtrace stopped: ' in line and CoreFile.__options['oneapi'] != 1:
logging.critical("GDB: Backtrace stopped")
if not force:
sys.exit(2)
Expand Down Expand Up @@ -512,6 +545,7 @@ def __init__(self):
self.arg_map["force"] = StatMergerArgs.StatMergerArgElement("r", False, int, 0, "whether to force parsing on warnings and errors")
self.arg_map["threads"] = StatMergerArgs.StatMergerArgElement("T", False, int, 1, "max number of threads")
self.arg_map["cuda"] = StatMergerArgs.StatMergerArgElement("C", False, int, 0, "set if running on cuda cores")
self.arg_map["oneapi"] = StatMergerArgs.StatMergerArgElement("n", False, int, 0, "set if using gdb-oneapi")

self.arg_map["jobid"] = self.StatMergerArgElement("j", False, None, None, "[LW] delineate traces based on Job ID in the core file")
self.arg_map["exe"] = StatMergerArgs.StatMergerArgElement("x", True, str, "NULL", "[LW] the executable path")
Expand Down Expand Up @@ -638,7 +672,7 @@ def STATmerge_main(arg_list):
if os.stat(filename).st_size == 0:
empty_files.append(filename)
else:
p = subprocess.Popen(['file', filename], stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='utf8')
p = subprocess.Popen(['file', filename], stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='utf8', universal_newlines=True)
output, error = p.communicate()
if output.find('core file') != -1:
file_types['full'] = True
Expand Down
1 change: 1 addition & 0 deletions scripts/gdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ def resume(self):
logging.info('GDB resume PID %d' %(self.pid))
command = "continue\n"
ret = self.subprocess.stdin.write(command)
self.subprocess.stdin.flush()
lines = self.readlines('Continuing')
logging.debug('%s' %(repr(lines)))
return check_lines(lines)
Expand Down
226 changes: 226 additions & 0 deletions scripts/oneapi_gdb.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
#!/bin/env python

"""@package STATview
Visualizes dot graphs outputted by STAT."""

__copyright__ = """Modifications Copyright (C) 2022 Intel Corporation
SPDX-License-Identifier: BSD-3-Clause"""
__license__ = """Produced by Intel Corporation for Lawrence Livermore National Security, LLC.
Written by M. Oguzhan Karakaya oguzhan.karakaya@intel.com
LLNL-CODE-750488.
All rights reserved.

This file is part of STAT. For details, see http://www.github.com/LLNL/STAT. Please also read STAT/LICENSE.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

Redistributions of source code must retain the above copyright notice, this list of conditions and the disclaimer below.
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the disclaimer (as noted below) in the documentation and/or other materials provided with the distribution.
Neither the name of the LLNS/LLNL 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 LAWRENCE LIVERMORE NATIONAL SECURITY, LLC, THE U.S. DEPARTMENT OF ENERGY 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.
"""
__author__ = ["M. Oguzhan Karakaya <oguzhan.karakaya@intel.com>"]
__version_major__ = 4
__version_minor__ = 1
__version_revision__ = 0
__version__ = "%d.%d.%d" %(__version_major__, __version_minor__, __version_revision__)

import sys
import os
import logging
import re
from gdb import GdbDriver, check_lines

def expand_simd_spec(simd):
"""
Converts a merged simd specifier into a list of individual simd lanes.
`simd` string is expected to be either an individual lane number
or a comma-separated range list inside square brackets.
"""
if simd[0] == '[':
simd = simd[1:]
lanes = []
for item1 in simd.split(" "):
for item2 in item1.split(","):
if item2.isnumeric():
lanes += item2
else:
pair = item2.split("-")
for num in range(int(pair[0]), int(pair[1])+1):
lanes += str(num)
return lanes

def parse_thread_info_line(line, parse_simd_lanes = False):
"""
Extracts thread IDs from a `thread info` output produced by
Intel(R) Distribution for GDB*. See oneapi_gdb_test.py for
sample inputs and outputs.
"""
tid_single_simd = re.compile(
r"^[\s|*]*(\d+(\.?\d+)?)(?:(?::(\d+))?)\s*(?:Thread|LWP)")
tid_multiple_simd = re.compile(
r"^[\s|*]*(\d+(\.?\d+)?)(?:(?::\[(.*?)\])?)\s*(?:Thread|LWP)")
match = re.search(tid_multiple_simd, line)
if not match:
return []
if not parse_simd_lanes:
return [ match[1] ]
if not match[3]:
match = re.search(tid_single_simd, line)
if not match[3]:
return [ match[1] ]
return [ match[1] + ":" + match[3]]
return [ match[1] + ":" + x for x in expand_simd_spec(match[3]) ]

def clean_cpp_template_brackets_and_call_signature(string):
"""
Prettify function name output by Intel(R) Distribution for GDB*.
Long template parameters and function signatures are collapsed.
"""
sub_strings = []
(found, begin, end, counter) = (False, 0, 0, 0)
for idx, char in enumerate(string):
if char == "<" and not found:
found = True
begin = idx
counter = 1
elif char == "<" and found:
counter += 1
elif char == ">" and found and counter > 1:
counter -= 1
elif char == ">" and found and counter == 1:
end = idx
counter -= 1
found = False
sub_strings.append(string[begin:end+1:])
# Removing template variables from the function name
for sub_string in sub_strings:
string = string.replace(sub_string, "<...>")
# Removing call signature for the function name
ptrn = re.compile(r"\([^()]+\)(?=[^()]*$)")
match = ptrn.findall(string)
if match:
string = string.replace(match[-1], "(...)")

return string

def parse_frameinfo_from_backtrace(string):
"""
Takes the gdb backtrace output and call
clean_cpp_template_brackets_and_call_signature function over
extracted function names. See oneapi_gdb_test.py for
sample inputs and outputs.
"""
function = 'Unknown'
source = '??'
linenum = 0
error = True

frame_fromat = re.compile(
r"^#\d+\s+(?:0x\S+)*(?: in )*(.*)\s\(.*\)\s(?:at|from)\s([^:]+)(?::)?(\d+)?$")
match = frame_fromat.match(string)
if match:
error = False
function, source, linenum = match.groups()
if linenum is None:
linenum = 0
else:
linenum = int(linenum)
function = clean_cpp_template_brackets_and_call_signature(function)
return {'function':function,
'source':source,
'linenum':linenum,
'error':error}

class OneAPIGdbDriver(GdbDriver):
""" A class to drive Intel(R) Distribution for GDB* """

gdb_command = 'gdb-oneapi'
parse_simd_lanes = False
if 'STAT_COLLECT_SIMD_BT' in os.environ and \
os.environ['STAT_COLLECT_SIMD_BT'] == "1":
parse_simd_lanes = True

def get_thread_list(self):
"""
Gets the list of threads in the target process. For
Intel(R) Distribution for GDB* this function extracts SIMD
lane information as part of a thread ID.
It is to improve the resulting representation by adding the
information on number of active SIMD lanes along with stack
trace information.
"""
logging.info('gdb-oneapi: info threads')
tids = []
lines = self.communicate("info threads")
logging.debug('%s', repr(lines))
for line in lines:
if "inactive" in line:
continue
tids += parse_thread_info_line(line, self.parse_simd_lanes)
return tids

def bt(self, thread_id):
"""
Gets a backtrace from the requested thread id.
returns list of frames, where each frame is a map of attributes.
"""
logging.info('GDB thread bt ID %s' %(thread_id))
ret = []

lines = self.communicate("thread apply %s bt" %thread_id)

lines = lines[2:]
for line_num, line in enumerate(lines):
if line[0] != '#':
continue
logging.debug('Parsing line #%d: %d', line_num, line)
ret.append(parse_frameinfo_from_backtrace(line))
return ret

def attach(self):
"""
Attaches to the target process and checkes if there is
an error related to gdbserver-gt at the gdb console output.
"""
logging.info('GDB attach to PID %d' %(self.pid))
lines = self.communicate("attach %d" %self.pid)
# We need to check if there is an error with starting
# gdbserver-gt and inform the user for possible missing
# device backtrace features.
ptrn = re.compile(
r"^intelgt\:\s*(\S*)\s*failed to start\.")
for line in lines:
match = ptrn.match(line)
if match:
gdbserver_name = match.groups()[0]
logging.error("""
OneAPI %s initialization failed for process %d. Device backtrace will not be available.""",
gdbserver_name, self.pid)
logging.debug('%s', repr(lines))
return check_lines(lines)

def target_core(self, path):
"""
Intel(R) Distribution for GDB* targets a core dump
file given by path.
"""
logging.info('GDB open core %s' %(path))
self.communicate("target core %s" %path)

if __name__ == "__main__":
gdb = OneAPIGdbDriver(0, 'debug', 'log.txt')
if gdb.launch() is False:
sys.stderr.write('gdb launch failed\n')
sys.exit(1)
gdb.target_core(sys.argv[1])
thread_ids = gdb.get_thread_list()
thread_ids.sort()
for tid in thread_ids:
bt = gdb.bt(tid)
print("Thread", tid)
for i, frame in enumerate(bt):
print("%d) %s:%s" %(i, frame['source'], frame['linenum']))
print()
gdb.close()
Loading