blob: de2e565d4ff1e22dbc70f35b5e338b2f0b8d5fb0 [file] [log] [blame]
"""
Finds and prints the crash report associated with a specific (binary filename, process id).
Waits (max_wait_time/attempts_remaining) between retries.
By default, max_wait_time=5 and retry_count=10, which results in a total wait time of ~15s
Errors if the report cannot be found after `retry_count` retries.
"""
import sys, os, argparse, re, glob, shutil, time
def main():
parser = argparse.ArgumentParser()
parser.add_argument(
"--pid",
type=str,
required=True,
help="The process id of the process that crashed",
)
parser.add_argument(
"--binary-filename",
type=str,
required=True,
help="The name of the file that crashed",
)
parser.add_argument(
"--retry-count",
type=int,
nargs="?",
default=10,
help="The number of retries to make",
)
parser.add_argument(
"--max-wait-time",
type=float,
nargs="?",
default=5.0,
help="The max amount of seconds to wait between tries",
)
parser.add_argument(
"--dir",
nargs="?",
type=str,
default="~/Library/Logs/DiagnosticReports",
help="The directory to look for the crash report",
)
parser.add_argument(
"--outfile",
nargs="?",
type=argparse.FileType("r"),
default=sys.stdout,
help="Where to write the result",
)
args = parser.parse_args()
assert args.pid, "pid can't be empty"
assert args.binary_filename, "binary-filename can't be empty"
os.chdir(os.path.expanduser(args.dir))
output_report_with_retries(
args.outfile,
args.pid.strip(),
args.binary_filename,
args.retry_count,
args.max_wait_time,
)
def output_report_with_retries(
outfile, pid, filename, attempts_remaining, max_wait_time
):
report_name = find_report_in_cur_dir(pid, filename)
if report_name:
with open(report_name, "r") as f:
shutil.copyfileobj(f, outfile)
return
elif attempts_remaining > 0:
# As the number of attempts remaining decreases, increase the number of seconds waited
# if the max wait time is 2s and there are 10 attempts remaining, wait .2 seconds.
# if the max wait time is 2s and there are 2 attempts remaining, wait 1 second.
time.sleep(max_wait_time / attempts_remaining)
output_report_with_retries(
outfile, pid, filename, attempts_remaining - 1, max_wait_time
)
else:
raise RuntimeError("Report not found for ({}, {}).".format(filename, pid))
def find_report_in_cur_dir(pid, filename):
for report_name in sorted(glob.glob("{}_*.crash".format(filename)), reverse=True):
# parse out pid from first line of report
# `Process: filename [pid]``
with open(report_name) as cur_report:
pattern = re.compile(r"Process: *{} \[([0-9]*)\]".format(filename))
cur_report_pid = pattern.search(cur_report.readline()).group(1)
assert cur_report_pid and cur_report_pid.isdigit()
if cur_report_pid == pid:
return report_name
# did not find the crash report
return None
if __name__ == "__main__":
main()