Skip to content

Instantly share code, notes, and snippets.

@Mart-Bogdan
Created April 9, 2026 22:19
Show Gist options
  • Select an option

  • Save Mart-Bogdan/99589167ff64c54fc72d971d232463c2 to your computer and use it in GitHub Desktop.

Select an option

Save Mart-Bogdan/99589167ff64c54fc72d971d232463c2 to your computer and use it in GitHub Desktop.
Converts UE stack traces (plain or LLDB format) to Rider-compatible format
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "pyperclip",
# ]
# ///
"""
Unreal Engine Stack Trace Converter
Converts UE stack traces (plain or LLDB format) to Rider-compatible format.
Usage:
# From file:
uv run ue_stack_converter.py stacktrace.txt
# From clipboard (read input AND copy result back to clipboard):
uv run ue_stack_converter.py --clipboard
# From stdin:
cat stacktrace.txt | uv run ue_stack_converter.py -
# Direct string argument:
uv run ue_stack_converter.py --text "GitSourceControlUtils::UpdateChangelistStateByCommand() GitSourceControlUtils.cpp:1636"
# Save output to file:
uv run ue_stack_converter.py stacktrace.txt -o converted.txt
"""
import re
import sys
import argparse
def convert_plain_frame(line: str) -> str | None:
"""
Converts a plain UE stack trace line like:
GitSourceControlUtils::UpdateChangelistStateByCommand() GitSourceControlUtils.cpp:1636
To:
at GitSourceControlUtils::UpdateChangelistStateByCommand() in C:\\GitSourceControlUtils.cpp:line 1636
"""
pattern = r'^(\[Inlined\]\s+)?(.+?)\s+([\w.]+\.(?:cpp|h|hpp|inl|c))\s*:\s*(\d+)\s*$'
m = re.match(pattern, line.strip())
if not m:
return None
inlined_prefix = m.group(1) or ''
func = m.group(2).strip()
filename = m.group(3)
lineno = m.group(4)
inlined_note = '[Inlined] ' if inlined_prefix else ''
return f' at {inlined_note}{func} in C:\\{filename}:line {lineno}'
def convert_lldb_frame(line: str) -> str | None:
"""
Rewrites only the trailing `at File.cpp:N` part of an LLDB frame line.
Everything else (frame number, address, DLL, function, parameter values) is kept verbatim.
Input:
frame #1: 0x00007ffb UnrealEditor.dll`Foo::Bar(x=1) at Foo.cpp:42
Output:
frame #1: 0x00007ffb UnrealEditor.dll`Foo::Bar(x=1) at C:\Foo.cpp:line 42
"""
pattern = r'(.*?\bat\s)([\w.]+\.(?:cpp|h|hpp|inl|c)):(\d+)\s*$'
m = re.match(pattern, line)
if not m:
return None
prefix = m.group(1)
filename = m.group(2)
lineno = m.group(3)
return f'{prefix}C:\\{filename}:line {lineno}'
def is_lldb_format(text: str) -> bool:
"""Detect if the input looks like LLDB format."""
return bool(re.search(r'frame\s+#\d+:', text))
def convert_stack_trace(text: str) -> str:
"""Convert a full stack trace (auto-detects plain UE vs LLDB format)."""
lines = text.splitlines()
output_lines = []
lldb_mode = is_lldb_format(text)
for line in lines:
if not line.strip():
output_lines.append('')
continue
if lldb_mode:
converted = convert_lldb_frame(line)
if converted:
output_lines.append(converted)
else:
output_lines.append(line)
else:
converted = convert_plain_frame(line)
if converted:
output_lines.append(converted)
else:
output_lines.append(line)
return '\n'.join(output_lines)
def main():
parser = argparse.ArgumentParser(
description='Convert Unreal Engine stack traces to Rider-compatible format.'
)
input_group = parser.add_mutually_exclusive_group()
input_group.add_argument(
'file',
nargs='?',
help='Input file path (use - for stdin)',
)
input_group.add_argument(
'--text', '-t',
help='Stack trace text provided directly as argument',
)
input_group.add_argument(
'--clipboard', '-c',
action='store_true',
help='Read stack trace from clipboard and copy result back to clipboard',
)
parser.add_argument(
'--output', '-o',
help='Output file path (default: print to stdout)',
)
args = parser.parse_args()
# --- Read input ---
if args.clipboard:
import pyperclip
raw = pyperclip.paste()
elif args.text:
raw = args.text
elif args.file == '-' or (not args.file and not sys.stdin.isatty()):
raw = sys.stdin.read()
elif args.file:
try:
with open(args.file, 'r', encoding='utf-8') as f:
raw = f.read()
except FileNotFoundError:
print(f'Error: file not found: {args.file}', file=sys.stderr)
sys.exit(1)
else:
print('Paste your stack trace below, then press Ctrl+D (Unix) or Ctrl+Z+Enter (Windows):')
raw = sys.stdin.read()
if not raw.strip():
print('Error: empty input.', file=sys.stderr)
sys.exit(1)
# --- Convert ---
result = convert_stack_trace(raw)
# --- Output ---
if args.output:
with open(args.output, 'w', encoding='utf-8') as f:
f.write(result)
print(f'Converted stack trace written to: {args.output}')
else:
print(result)
if args.clipboard:
import pyperclip
pyperclip.copy(result)
print('\n(Result copied to clipboard)', file=sys.stderr)
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment