mirror of
https://github.com/frida/frida-python
synced 2026-06-08 14:16:17 +00:00
Merge branch 'feature/spawn'
This commit is contained in:
+72
-13
@@ -2,6 +2,7 @@
|
||||
|
||||
import collections
|
||||
from optparse import OptionParser
|
||||
import os
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
@@ -27,6 +28,17 @@ class ConsoleApplication(object):
|
||||
action='store_const', const='tether', dest="device_type", default='local')
|
||||
parser.add_option("-R", "--remote", help="connect to remote device",
|
||||
action='store_const', const='remote', dest="device_type", default='local')
|
||||
if self._needs_target():
|
||||
def store_target(option, opt_str, target_value, parser, target_type, *args, **kwargs):
|
||||
if target_type == 'file':
|
||||
target_value = [target_value]
|
||||
setattr(parser.values, 'target', (target_type, target_value))
|
||||
parser.add_option("-f", "--file", help="spawn FILE", metavar="FILE",
|
||||
type='string', action='callback', callback=store_target, callback_args=('file',))
|
||||
parser.add_option("-n", "--attach-name", help="attach to NAME", metavar="NAME",
|
||||
type='string', action='callback', callback=store_target, callback_args=('name',))
|
||||
parser.add_option("-p", "--attach-pid", help="attach to PID", metavar="PID",
|
||||
type='int', action='callback', callback=store_target, callback_args=('pid',))
|
||||
self._add_options(parser)
|
||||
|
||||
(options, args) = parser.parse_args()
|
||||
@@ -34,25 +46,36 @@ class ConsoleApplication(object):
|
||||
self._device_type = options.device_type
|
||||
self._device = None
|
||||
self._schedule_on_device_lost = lambda: self._reactor.schedule(self._on_device_lost)
|
||||
self._target = None
|
||||
self._spawned_pid = None
|
||||
self._spawned_argv = None
|
||||
self._process = None
|
||||
self._schedule_on_process_detached = lambda: self._reactor.schedule(self._on_process_detached)
|
||||
self._started = False
|
||||
self._resumed = False
|
||||
self._reactor = Reactor(run_until_return)
|
||||
self._exit_status = None
|
||||
self._status_updated = False
|
||||
|
||||
self._initialize(parser, options, args)
|
||||
|
||||
target_specifier = self._target_specifier(parser, options, args)
|
||||
if target_specifier is not None:
|
||||
try:
|
||||
self._target = int(target_specifier)
|
||||
except:
|
||||
self._target = target_specifier
|
||||
if self._needs_target():
|
||||
target = getattr(options, 'target', None)
|
||||
if target is None:
|
||||
if len(args) < 1:
|
||||
parser.error("target file, process name or pid must be specified")
|
||||
target = infer_target(args[0])
|
||||
args.pop(0)
|
||||
target = expand_target(target)
|
||||
if target[0] == 'file':
|
||||
argv = target[1]
|
||||
if not os.path.isfile(argv[0]):
|
||||
parser.error("%s: file not found" % argv[0])
|
||||
argv.extend(args)
|
||||
args = []
|
||||
self._target = target
|
||||
else:
|
||||
self._target = None
|
||||
|
||||
self._initialize(parser, options, args)
|
||||
|
||||
def run(self):
|
||||
mgr = frida.get_device_manager()
|
||||
on_devices_changed = lambda: self._reactor.schedule(self._try_start)
|
||||
@@ -66,6 +89,8 @@ class ConsoleApplication(object):
|
||||
self._process.off('detached', self._schedule_on_process_detached)
|
||||
self._process.detach()
|
||||
self._process = None
|
||||
if self._spawned_pid is not None:
|
||||
self._device.kill(self._spawned_pid)
|
||||
if self._device is not None:
|
||||
self._device.off('lost', self._schedule_on_device_lost)
|
||||
mgr.off('changed', on_devices_changed)
|
||||
@@ -78,8 +103,8 @@ class ConsoleApplication(object):
|
||||
def _initialize(self, parser, options, args):
|
||||
pass
|
||||
|
||||
def _target_specifier(self, parser, options, args):
|
||||
return None
|
||||
def _needs_target(self):
|
||||
return False
|
||||
|
||||
def _start(self):
|
||||
pass
|
||||
@@ -87,6 +112,13 @@ class ConsoleApplication(object):
|
||||
def _stop(self):
|
||||
pass
|
||||
|
||||
def _resume(self):
|
||||
if self._resumed:
|
||||
return
|
||||
if self._spawned_pid is not None:
|
||||
self._device.resume(self._spawned_pid)
|
||||
self._resumed = True
|
||||
|
||||
def _exit(self, exit_status):
|
||||
self._exit_status = exit_status
|
||||
self._reactor.stop()
|
||||
@@ -100,8 +132,17 @@ class ConsoleApplication(object):
|
||||
self._device.on('lost', self._schedule_on_device_lost)
|
||||
if self._target is not None:
|
||||
try:
|
||||
self._update_status("Attaching...")
|
||||
self._process = self._device.attach(self._target)
|
||||
target_type, target_value = self._target
|
||||
if target_type == 'file':
|
||||
argv = target_value
|
||||
self._update_status("Spawning `%s`..." % " ".join(argv))
|
||||
self._spawned_pid = self._device.spawn(argv)
|
||||
self._spawned_argv = argv
|
||||
attach_target = self._spawned_pid
|
||||
else:
|
||||
attach_target = target_value
|
||||
self._update_status("Attaching...")
|
||||
self._process = self._device.attach(attach_target)
|
||||
self._process.on('detached', self._schedule_on_process_detached)
|
||||
except Exception as e:
|
||||
self._update_status("Failed to attach: %s" % e)
|
||||
@@ -138,6 +179,24 @@ def find_device(type):
|
||||
return device
|
||||
return None
|
||||
|
||||
def infer_target(target_value):
|
||||
if target_value.startswith('.') or target_value.startswith(os.path.sep):
|
||||
target_type = 'file'
|
||||
target_value = [target_value]
|
||||
else:
|
||||
try:
|
||||
target_value = int(target_value)
|
||||
target_type = 'pid'
|
||||
except:
|
||||
target_type = 'name'
|
||||
return (target_type, target_value)
|
||||
|
||||
def expand_target(target):
|
||||
target_type, target_value = target
|
||||
if target_type == 'file':
|
||||
target_value = [os.path.abspath(target_value[0])]
|
||||
return (target_type, target_value)
|
||||
|
||||
|
||||
class Reactor(object):
|
||||
def __init__(self, run_until_return):
|
||||
|
||||
+96
-95
@@ -1,6 +1,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from frida.application import await_enter
|
||||
from frida.core import ModuleFunction
|
||||
import threading
|
||||
|
||||
|
||||
class Discoverer(object):
|
||||
@@ -13,9 +15,16 @@ class Discoverer(object):
|
||||
self._reactor.schedule(lambda: self._process_message(message, data, process, ui))
|
||||
source = self._create_discover_script()
|
||||
self._script = process.session.create_script(source)
|
||||
self._script.on("message", on_message)
|
||||
self._script.on('message', on_message)
|
||||
self._script.load()
|
||||
|
||||
def stop(self):
|
||||
self._script.post_message({
|
||||
'to': "/sampler",
|
||||
'name': '+stop',
|
||||
'payload': {}
|
||||
})
|
||||
|
||||
def stop(self):
|
||||
if self._script is not None:
|
||||
try:
|
||||
@@ -25,90 +34,74 @@ class Discoverer(object):
|
||||
self._script = None
|
||||
|
||||
def _create_discover_script(self):
|
||||
return """
|
||||
var Sampler = function Sampler() {
|
||||
var total = 0;
|
||||
var pending = [];
|
||||
var active = [];
|
||||
var samples = {};
|
||||
Process.enumerateThreads({
|
||||
onMatch: function (thread) {
|
||||
pending.push(thread);
|
||||
},
|
||||
onComplete: function () {
|
||||
var currentThreadId = Process.getCurrentThreadId();
|
||||
pending = pending.filter(function (thread) {
|
||||
return thread.id !== currentThreadId;
|
||||
});
|
||||
total = pending.length;
|
||||
var processNext = function processNext() {
|
||||
active.forEach(function (thread) {
|
||||
Stalker.unfollow(thread.id);
|
||||
});
|
||||
active = pending.splice(0, 4);
|
||||
if (active.length > 0) {
|
||||
var begin = total - pending.length - active.length;
|
||||
send({
|
||||
from: "/sampler",
|
||||
name: '+progress',
|
||||
payload: {
|
||||
begin: begin,
|
||||
end: begin + active.length - 1,
|
||||
total: total
|
||||
}
|
||||
});
|
||||
} else {
|
||||
for (var address in samples) {
|
||||
if (samples.hasOwnProperty(address)) {
|
||||
var counts = samples[address].counts;
|
||||
var sum = 0;
|
||||
for (var i = 0; i !== counts.length; i++) {
|
||||
sum += counts[i];
|
||||
}
|
||||
var callsPerSecond = Math.round(sum / (counts.length * 0.25));
|
||||
samples[address] = callsPerSecond;
|
||||
}
|
||||
}
|
||||
send({
|
||||
from: "/sampler",
|
||||
name: '+result',
|
||||
payload: {
|
||||
samples: samples
|
||||
}
|
||||
});
|
||||
samples = null;
|
||||
}
|
||||
active.forEach(function (thread) {
|
||||
Stalker.follow(thread.id, {
|
||||
return """\
|
||||
function Sampler() {
|
||||
var threadIds = [];
|
||||
var result = {};
|
||||
|
||||
function onStanza(stanza) {
|
||||
if (stanza.to === "/sampler") {
|
||||
if (stanza.name === '+stop') {
|
||||
stop();
|
||||
}
|
||||
}
|
||||
|
||||
recv(onStanza);
|
||||
}
|
||||
|
||||
this.start = function () {
|
||||
threadIds = [];
|
||||
Process.enumerateThreads({
|
||||
onMatch: function (thread) {
|
||||
threadIds.push(thread.id);
|
||||
},
|
||||
onComplete: function () {
|
||||
threadIds.forEach(function (threadId) {
|
||||
Stalker.follow(threadId, {
|
||||
events: { call: true },
|
||||
onCallSummary: function (summary) {
|
||||
if (samples === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (var address in summary) {
|
||||
if (summary.hasOwnProperty(address)) {
|
||||
var sample = samples[address];
|
||||
if (sample === undefined) {
|
||||
sample = { counts: [] };
|
||||
samples[address] = sample;
|
||||
}
|
||||
sample.counts.push(summary[address]);
|
||||
var count = result[address] || 0;
|
||||
result[address] = count + summary[address];
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
if (active.length > 0) {
|
||||
setTimeout(processNext, 2000);
|
||||
setTimeout(Stalker.garbageCollect, 2100);
|
||||
}
|
||||
};
|
||||
setTimeout(processNext, 0);
|
||||
}
|
||||
});
|
||||
|
||||
send({
|
||||
from: "/sampler",
|
||||
name: '+started',
|
||||
payload: {
|
||||
total: threadIds.length
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function stop() {
|
||||
threadIds.forEach(function (threadId) {
|
||||
Stalker.unfollow(threadId);
|
||||
});
|
||||
threadIds = [];
|
||||
|
||||
send({
|
||||
from: "/sampler",
|
||||
name: '+stopped',
|
||||
payload: {
|
||||
result: result
|
||||
}
|
||||
});
|
||||
result = {};
|
||||
}
|
||||
|
||||
recv(onStanza);
|
||||
};
|
||||
|
||||
sampler = new Sampler();
|
||||
setTimeout(function () { sampler.start(); }, 0);
|
||||
"""
|
||||
|
||||
def _process_message(self, message, data, process, ui):
|
||||
@@ -117,21 +110,21 @@ sampler = new Sampler();
|
||||
name = stanza['name']
|
||||
payload = stanza['payload']
|
||||
if stanza['from'] == "/sampler":
|
||||
if name == '+progress':
|
||||
ui.on_sample_progress(payload['begin'], payload['end'], payload['total'])
|
||||
elif name == '+result':
|
||||
if name == '+started':
|
||||
ui.on_sample_start(payload['total'])
|
||||
elif name == '+stopped':
|
||||
module_functions = {}
|
||||
dynamic_functions = []
|
||||
for address, rate in payload['samples'].items():
|
||||
for address, count in payload['result'].items():
|
||||
address = int(address, 16)
|
||||
function = process.ensure_function(address)
|
||||
if isinstance(function, ModuleFunction):
|
||||
functions = module_functions.get(function.module, [])
|
||||
if len(functions) == 0:
|
||||
module_functions[function.module] = functions
|
||||
functions.append((function, rate))
|
||||
functions.append((function, count))
|
||||
else:
|
||||
dynamic_functions.append((function, rate))
|
||||
dynamic_functions.append((function, count))
|
||||
ui.on_sample_result(module_functions, dynamic_functions)
|
||||
else:
|
||||
print(message, data)
|
||||
@@ -141,7 +134,7 @@ sampler = new Sampler();
|
||||
print(message, data)
|
||||
|
||||
class UI(object):
|
||||
def on_sample_progress(self, begin, end, total):
|
||||
def on_sample_start(self, total):
|
||||
pass
|
||||
|
||||
def on_sample_result(self, module_functions, dynamic_functions):
|
||||
@@ -152,16 +145,23 @@ def main():
|
||||
from frida.application import ConsoleApplication
|
||||
|
||||
class DiscovererApplication(ConsoleApplication, UI):
|
||||
def __init__(self):
|
||||
self._results_received = threading.Event()
|
||||
ConsoleApplication.__init__(self, self._await_keys)
|
||||
|
||||
def _await_keys(self):
|
||||
await_enter()
|
||||
self._reactor.schedule(lambda: self._discoverer.stop())
|
||||
self._results_received.wait()
|
||||
|
||||
def _usage(self):
|
||||
return "usage: %prog [options] process-name-or-id"
|
||||
return "usage: %prog [options] target"
|
||||
|
||||
def _initialize(self, parser, options, args):
|
||||
self._discoverer = None
|
||||
|
||||
def _target_specifier(self, parser, options, args):
|
||||
if len(args) != 1:
|
||||
parser.error("process name or id must be specified")
|
||||
return args[0]
|
||||
def _needs_target(self):
|
||||
return True
|
||||
|
||||
def _start(self):
|
||||
self._update_status("Injecting script...")
|
||||
@@ -173,24 +173,25 @@ def main():
|
||||
self._discoverer.stop()
|
||||
self._discoverer = None
|
||||
|
||||
def on_sample_progress(self, begin, end, total):
|
||||
self._update_status("Sampling %d threads: %d through %d..." % (total, begin, end))
|
||||
def on_sample_start(self, total):
|
||||
self._update_status("Tracing %d threads. Press ENTER to stop." % total)
|
||||
self._resume()
|
||||
|
||||
def on_sample_result(self, module_functions, dynamic_functions):
|
||||
for module, functions in module_functions.items():
|
||||
print(module.name)
|
||||
print("\t%-10s\t%s" % ("Rate", "Function"))
|
||||
for function, rate in sorted(functions, key=lambda item: item[1], reverse=True):
|
||||
print("\t%-10d\t%s" % (rate, function))
|
||||
print("\t%-10s\t%s" % ("Calls", "Function"))
|
||||
for function, count in sorted(functions, key=lambda item: item[1], reverse=True):
|
||||
print("\t%-10d\t%s" % (count, function))
|
||||
print("")
|
||||
|
||||
if len(dynamic_functions) > 0:
|
||||
print("Dynamic functions:")
|
||||
print("\t%-10s\t%s" % ("Rate", "Function"))
|
||||
for function, rate in sorted(dynamic_functions, key=lambda item: item[1], reverse=True):
|
||||
print("\t%-10d\t%s" % (rate, function))
|
||||
print("\t%-10s\t%s" % ("Calls", "Function"))
|
||||
for function, count in sorted(dynamic_functions, key=lambda item: item[1], reverse=True):
|
||||
print("\t%-10d\t%s" % (count, function))
|
||||
|
||||
self._exit(0)
|
||||
self._results_received.set()
|
||||
|
||||
app = DiscovererApplication()
|
||||
app.run()
|
||||
|
||||
+70
-52
@@ -17,12 +17,10 @@ def main():
|
||||
super(REPLApplication, self).__init__(self._process_input)
|
||||
|
||||
def _usage(self):
|
||||
return "usage: %prog [options]"
|
||||
return "usage: %prog [options] target"
|
||||
|
||||
def _target_specifier(self, parser, options, args):
|
||||
if len(args) != 1:
|
||||
parser.error("process name or id must be specified")
|
||||
return args[0]
|
||||
def _needs_target(self):
|
||||
return True
|
||||
|
||||
def _start(self):
|
||||
def on_message(message, data):
|
||||
@@ -30,50 +28,62 @@ def main():
|
||||
self._script = self._process.session.create_script(self._create_repl_script())
|
||||
self._script.on('message', on_message)
|
||||
self._script.load()
|
||||
if self._spawned_argv is not None:
|
||||
self._update_status("Spawned `%s`. Call resume() to let the main thread start executing!" % " ".join(self._spawned_argv))
|
||||
self._idle.set()
|
||||
|
||||
def _stop(self):
|
||||
self._script.unload()
|
||||
try:
|
||||
self._script.unload()
|
||||
except:
|
||||
pass
|
||||
self._script = None
|
||||
|
||||
def _create_repl_script(self):
|
||||
return """\
|
||||
|
||||
Object.defineProperty(this, 'modules', {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
var result = [];
|
||||
Process.enumerateModules({
|
||||
onMatch: function onMatch(mod) {
|
||||
result.push(mod);
|
||||
},
|
||||
onComplete: function onComplete() {
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
});
|
||||
(function () {
|
||||
this.resume = function () {
|
||||
send({ name: '+resume' });
|
||||
};
|
||||
|
||||
function onExpression(expression) {
|
||||
try {
|
||||
var result;
|
||||
eval("result = " + expression);
|
||||
var sentRaw = false;
|
||||
if (result && result.hasOwnProperty('length')) {
|
||||
try {
|
||||
send({ name: '+result', payload: "OOB" }, result);
|
||||
sentRaw = true;
|
||||
} catch (e) {
|
||||
Object.defineProperty(this, 'modules', {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
var result = [];
|
||||
Process.enumerateModules({
|
||||
onMatch: function onMatch(mod) {
|
||||
result.push(mod);
|
||||
},
|
||||
onComplete: function onComplete() {
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
});
|
||||
|
||||
function onExpression(expression) {
|
||||
try {
|
||||
var result;
|
||||
eval("result = " + expression);
|
||||
var sentRaw = false;
|
||||
if (result && result.hasOwnProperty('length')) {
|
||||
try {
|
||||
send({ name: '+result', payload: "OOB" }, result);
|
||||
sentRaw = true;
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
if (!sentRaw) {
|
||||
send({ name: '+result', payload: result });
|
||||
}
|
||||
} catch (e) {
|
||||
send({ name: '+error', payload: e.toString() });
|
||||
}
|
||||
if (!sentRaw) {
|
||||
send({ name: '+result', payload: result });
|
||||
}
|
||||
} catch (e) {
|
||||
send({ name: '+error', payload: e.toString() });
|
||||
recv(onExpression);
|
||||
}
|
||||
recv(onExpression);
|
||||
}
|
||||
recv(onExpression);
|
||||
}).call(this);
|
||||
"""
|
||||
|
||||
def _process_input(self):
|
||||
@@ -111,22 +121,30 @@ recv(onExpression);
|
||||
|
||||
if message['type'] == 'send' and 'payload' in message:
|
||||
stanza = message['payload']
|
||||
if isinstance(stanza, dict) and stanza.get('name') in ('+result', '+error'):
|
||||
handled = True
|
||||
if data is not None:
|
||||
output = hexdump(data).rstrip("\n")
|
||||
else:
|
||||
if 'payload' in stanza:
|
||||
value = stanza['payload']
|
||||
if stanza['name'] == '+result':
|
||||
output = json.dumps(value, sort_keys=True, indent=4, separators=(",", ": "))
|
||||
else:
|
||||
output = value
|
||||
if isinstance(stanza, dict):
|
||||
name = stanza.get('name')
|
||||
if name in ('+result', '+error'):
|
||||
if data is not None:
|
||||
output = hexdump(data).rstrip("\n")
|
||||
else:
|
||||
output = "undefined"
|
||||
sys.stdout.write(output + "\n")
|
||||
sys.stdout.flush()
|
||||
self._idle.set()
|
||||
if 'payload' in stanza:
|
||||
value = stanza['payload']
|
||||
if stanza['name'] == '+result':
|
||||
output = json.dumps(value, sort_keys=True, indent=4, separators=(",", ": "))
|
||||
else:
|
||||
output = value
|
||||
else:
|
||||
output = "undefined"
|
||||
sys.stdout.write(output + "\n")
|
||||
sys.stdout.flush()
|
||||
self._idle.set()
|
||||
|
||||
handled = True
|
||||
elif name == '+resume':
|
||||
self._resume()
|
||||
self._idle.set()
|
||||
|
||||
handled = True
|
||||
|
||||
if not handled:
|
||||
print("message:", message, "data:", data)
|
||||
|
||||
+45
-28
@@ -136,7 +136,7 @@ class Tracer(object):
|
||||
ui.on_trace_progress('resolve')
|
||||
working_set = self._profile.resolve(process)
|
||||
source = self._create_trace_script()
|
||||
ui.on_trace_progress('upload')
|
||||
ui.on_trace_progress('instrument')
|
||||
self._script = process.session.create_script(source)
|
||||
self._script.on('message', on_message)
|
||||
self._script.load()
|
||||
@@ -152,7 +152,12 @@ class Tracer(object):
|
||||
'items': targets
|
||||
}
|
||||
})
|
||||
ui.on_trace_progress('ready')
|
||||
|
||||
self._script.post_message({
|
||||
'to': "/targets",
|
||||
'name': '+start',
|
||||
'payload': {}
|
||||
})
|
||||
|
||||
return working_set
|
||||
|
||||
@@ -176,6 +181,8 @@ function onStanza(stanza) {
|
||||
add(stanza.payload.items);
|
||||
} else if (stanza.name === '+update') {
|
||||
update(stanza.payload.items);
|
||||
} else if (stanza.name === '+start') {
|
||||
start();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,6 +227,16 @@ function update(targets) {
|
||||
handlers[target.absolute_address][0] = handler;
|
||||
});
|
||||
}
|
||||
function start() {
|
||||
pending.push(function acknowledgeStart() {
|
||||
send({
|
||||
from: "/targets",
|
||||
name: '+started',
|
||||
payload: {}
|
||||
});
|
||||
});
|
||||
scheduleNext();
|
||||
}
|
||||
function scheduleNext() {
|
||||
if (timer === null) {
|
||||
timer = setTimeout(processNext, 0);
|
||||
@@ -238,22 +255,23 @@ recv(onStanza);
|
||||
"""
|
||||
|
||||
def _process_message(self, message, data, ui):
|
||||
handled = False
|
||||
if message['type'] == 'send':
|
||||
stanza = message['payload']
|
||||
if stanza['from'] == "/events":
|
||||
if stanza['name'] == '+add':
|
||||
events = [(timestamp, int(target_address.rstrip("L"), 16), message) for timestamp, target_address, message in stanza['payload']['items']]
|
||||
if stanza['from'] == "/events" and stanza['name'] == '+add':
|
||||
events = [(timestamp, int(target_address.rstrip("L"), 16), message) for timestamp, target_address, message in stanza['payload']['items']]
|
||||
|
||||
ui.on_trace_events(events)
|
||||
ui.on_trace_events(events)
|
||||
|
||||
target_addresses = set([target_address for timestamp, target_address, message in events])
|
||||
for target_address in target_addresses:
|
||||
self._repository.sync_handler(target_address)
|
||||
else:
|
||||
print(stanza)
|
||||
else:
|
||||
print(stanza)
|
||||
else:
|
||||
target_addresses = set([target_address for timestamp, target_address, message in events])
|
||||
for target_address in target_addresses:
|
||||
self._repository.sync_handler(target_address)
|
||||
|
||||
handled = True
|
||||
elif stanza['from'] == "/targets" and stanza['name'] == '+started':
|
||||
ui.on_trace_progress('ready')
|
||||
handled = True
|
||||
if not handled:
|
||||
print(message)
|
||||
|
||||
class Repository(object):
|
||||
@@ -449,25 +467,19 @@ def main():
|
||||
self._profile_builder = pb
|
||||
|
||||
def _usage(self):
|
||||
return "usage: %prog [options] process-name-or-id"
|
||||
return "usage: %prog [options] target"
|
||||
|
||||
def _initialize(self, parser, options, args):
|
||||
self._tracer = None
|
||||
self._targets = None
|
||||
self._profile = self._profile_builder.build()
|
||||
|
||||
def _target_specifier(self, parser, options, args):
|
||||
if len(args) != 1:
|
||||
parser.error("process name or id must be specified")
|
||||
return args[0]
|
||||
def _needs_target(self):
|
||||
return True
|
||||
|
||||
def _start(self):
|
||||
self._tracer = Tracer(self._reactor, FileRepository(), self._profile)
|
||||
targets = self._tracer.start_trace(self._process, self)
|
||||
if len(targets) == 1:
|
||||
plural = ""
|
||||
else:
|
||||
plural = "s"
|
||||
self._update_status("Started tracing %d function%s. Press ENTER to stop." % (len(targets), plural))
|
||||
self._targets = self._tracer.start_trace(self._process, self)
|
||||
|
||||
def _stop(self):
|
||||
print("Stopping...")
|
||||
@@ -477,10 +489,15 @@ def main():
|
||||
def on_trace_progress(self, operation):
|
||||
if operation == 'resolve':
|
||||
self._update_status("Resolving functions...")
|
||||
elif operation == 'upload':
|
||||
self._update_status("Uploading data...")
|
||||
elif operation == 'instrument':
|
||||
self._update_status("Instrumenting functions...")
|
||||
elif operation == 'ready':
|
||||
self._update_status("Ready!")
|
||||
if len(self._targets) == 1:
|
||||
plural = ""
|
||||
else:
|
||||
plural = "s"
|
||||
self._update_status("Started tracing %d function%s. Press ENTER to stop." % (len(self._targets), plural))
|
||||
self._resume()
|
||||
|
||||
def on_trace_events(self, events):
|
||||
self._status_updated = False
|
||||
|
||||
Reference in New Issue
Block a user