Skip to content

Commit 6cdae0e

Browse files
authored
Merge pull request #285 from cr-dani-koretsky/profile-existing-process
mprof attach option - profile existing process
2 parents 68f2950 + aab7597 commit 6cdae0e

1 file changed

Lines changed: 58 additions & 22 deletions

File tree

mprof.py

Lines changed: 58 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,20 @@
88
import math
99
import logging
1010
import itertools
11+
from ast import literal_eval
1112

1213
from collections import defaultdict
1314
from argparse import ArgumentParser, ArgumentError, REMAINDER, RawTextHelpFormatter
1415

1516
import importlib
1617
import memory_profiler as mp
1718

18-
ALL_ACTIONS = ("run", "rm", "clean", "list", "plot")
19+
ALL_ACTIONS = ("run", "rm", "clean", "list", "plot", "attach")
1920
help_msg = """
2021
Available commands:
2122
2223
run run a given command or python file
24+
attach alias for 'run --attach': attach to an existing process by pid or name
2325
rm remove a given file generated by mprof
2426
clean clean the current directory from files created by mprof
2527
list display existing profiles, with indices
@@ -176,6 +178,16 @@ def get_cmd_line(args):
176178
args = [s if blanks.isdisjoint(s) else "'" + s + "'" for s in args]
177179
return ' '.join(args)
178180

181+
def find_first_process(name):
182+
for i in mp.psutil.process_iter():
183+
if name in i.name():
184+
return i
185+
return None
186+
187+
def attach_action():
188+
argv = sys.argv
189+
sys.argv = argv[:1] + ['--attach'] + argv[1:]
190+
run_action()
179191

180192
def run_action():
181193
import time, subprocess
@@ -192,6 +204,10 @@ def run_action():
192204
parser.add_argument("--multiprocess", "-M", dest="multiprocess", action="store_true",
193205
help="""Monitors forked processes creating individual plots for each child (disables --python features)""")
194206
parser.add_argument("--exit-code", "-E", dest="exit_code", action="store_true", help="""Propagate the exit code""")
207+
attach_arg = parser.add_argument("--attach", "-a", dest="attach_existing", action="store_true",
208+
help="Attach to an existing process, by process name or by pid")
209+
parser.add_argument("--timeout", "-t", dest="timeout", action="store", type=int,
210+
help="timeout in seconds for the profiling, default new process has no timeout, attach existing is 1 hour")
195211
parser.add_argument("--output", "-o", dest="filename",
196212
default="mprofile_%s.dat" % time.strftime("%Y%m%d%H%M%S", time.localtime()),
197213
help="""File to store results in, defaults to 'mprofile_<YYYYMMDDhhmmss>.dat' in the current directory,
@@ -214,34 +230,53 @@ def run_action():
214230

215231
mprofile_output = args.filename
216232

217-
# .. TODO: more than one script as argument ? ..
218233
program = args.program
219-
if program[0].endswith('.py') and not args.nopython:
220-
if args.multiprocess:
221-
# in multiprocessing mode you want to spawn a separate
222-
# python process
234+
if args.attach_existing:
235+
print('attaching to existing process, using hint: {}'.format(program[0]))
236+
if program[0].isdigit():
237+
p = literal_eval(program[0])
238+
cmd_line = get_cmd_line(program)
239+
else:
240+
proc = find_first_process(program[0])
241+
if proc is None:
242+
raise ArgumentError(attach_arg, '\nWhen attaching, program should be process name or pid.\nFailed to find a process using hint: {}'.format(program[0]))
243+
244+
p = proc.pid
245+
try:
246+
cmd_line = proc.cmdline()
247+
except:
248+
cmd_line = get_cmd_line(program)
249+
if args.timeout is None:
250+
args.timeout = 3600
251+
else:
252+
print('running new process')
253+
# .. TODO: more than one script as argument ? ..
254+
if program[0].endswith('.py') and not args.nopython:
255+
if args.multiprocess:
256+
# in multiprocessing mode you want to spawn a separate
257+
# python process
258+
if not program[0].startswith("python"):
259+
program.insert(0, sys.executable)
260+
args.python = False
261+
else:
262+
args.python = True
263+
if args.python:
264+
print("running as a Python program...")
223265
if not program[0].startswith("python"):
224266
program.insert(0, sys.executable)
225-
args.python = False
267+
cmd_line = get_cmd_line(program)
268+
extra_args = ["-m", "memory_profiler", "--timestamp", "-o", mprofile_output]
269+
if args.include_children:
270+
extra_args.append("--include-children")
271+
program[1:1] = extra_args
272+
p = subprocess.Popen(program)
226273
else:
227-
args.python = True
228-
if args.python:
229-
print("running as a Python program...")
230-
if not program[0].startswith("python"):
231-
program.insert(0, sys.executable)
232-
cmd_line = get_cmd_line(program)
233-
extra_args = ["-m", "memory_profiler", "--timestamp", "-o", mprofile_output]
234-
if args.include_children:
235-
extra_args.append("--include-children")
236-
program[1:1] = extra_args
237-
p = subprocess.Popen(program)
238-
else:
239-
cmd_line = get_cmd_line(program)
240-
p = subprocess.Popen(program)
274+
cmd_line = get_cmd_line(program)
275+
p = subprocess.Popen(program)
241276

242277
with open(mprofile_output, "a") as f:
243278
f.write("CMDLINE {0}\n".format(cmd_line))
244-
mp.memory_usage(proc=p, interval=args.interval, timestamps=True,
279+
mp.memory_usage(proc=p, interval=args.interval, timeout=args.timeout, timestamps=True,
245280
include_children=args.include_children,
246281
multiprocess=args.multiprocess, stream=f)
247282

@@ -837,6 +872,7 @@ def main():
837872
"clean": clean_action,
838873
"list": list_action,
839874
"run": run_action,
875+
"attach": attach_action,
840876
"plot": plot_action}
841877
actions[get_action()]()
842878

0 commit comments

Comments
 (0)