Added an option `--detect_deadlocks` which can help to identify an exact state of application's threads in case of a deadlock. It works like this: The user specifies the amount of time ios-deploy runs the app as usual. When the timeout is elapsed ios-deploy starts to print call-stacks of all threads every 5 seconds and the app keeps running. Comparing threads' call-stacks between each other helps to identify the threads which were stuck.
This commit is contained in:
Родитель
35c2c55fd1
Коммит
39449238ae
|
@ -92,6 +92,7 @@ If you are *not* using a node version manager like [nvm](https://github.com/crea
|
|||
-e, --exists check if the app with given bundle_id is installed or not
|
||||
-B, --list_bundle_id list bundle_id
|
||||
-W, --no-wifi ignore wifi devices
|
||||
--detect_deadlocks <sec> start printing backtraces for all threads periodically after specific amount of seconds
|
||||
|
||||
## Examples
|
||||
|
||||
|
@ -142,3 +143,6 @@ The included demo.app represents the minimum required to get code running on iOS
|
|||
|
||||
* `make demo.app` will generate the demo.app executable. If it doesn't compile, modify `IOS_SDK_VERSION` in the Makefile.
|
||||
* `make debug` will install demo.app and launch a LLDB session.
|
||||
|
||||
## Notes
|
||||
* `--detect_deadlocks` can help to identify an exact state of application's threads in case of a deadlock. It works like this: The user specifies the amount of time ios-deploy runs the app as usual. When the timeout is elapsed ios-deploy starts to print call-stacks of all threads every 5 seconds and the app keeps running. Comparing threads' call-stacks between each other helps to identify the threads which were stuck.
|
||||
|
|
|
@ -88,6 +88,7 @@ char *device_id = NULL;
|
|||
char *args = NULL;
|
||||
char *list_root = NULL;
|
||||
int _timeout = 0;
|
||||
int _detectDeadlockTimeout = 0;
|
||||
int port = 0; // 0 means "dynamically assigned"
|
||||
CFStringRef last_path = NULL;
|
||||
service_conn_t gdbfd;
|
||||
|
@ -615,9 +616,14 @@ void write_lldb_prep_cmds(AMDeviceRef device, CFURLRef disk_app_url) {
|
|||
CFMutableStringRef pmodule = CFStringCreateMutableCopy(NULL, 0, (CFStringRef)LLDB_FRUITSTRAP_MODULE);
|
||||
|
||||
CFRange rangeLLDB = { 0, CFStringGetLength(pmodule) };
|
||||
|
||||
CFStringRef exitcode_app_crash_str = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), exitcode_app_crash);
|
||||
CFStringFindAndReplace(pmodule, CFSTR("{exitcode_app_crash}"), exitcode_app_crash_str, rangeLLDB, 0);
|
||||
rangeLLDB.length = CFStringGetLength(pmodule);
|
||||
|
||||
CFStringRef detect_deadlock_timeout_str = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), _detectDeadlockTimeout);
|
||||
CFStringFindAndReplace(pmodule, CFSTR("{detect_deadlock_timeout}"), detect_deadlock_timeout_str, rangeLLDB, 0);
|
||||
rangeLLDB.length = CFStringGetLength(pmodule);
|
||||
|
||||
if (args) {
|
||||
CFStringRef cf_args = CFStringCreateWithCString(NULL, args, kCFStringEncodingUTF8);
|
||||
|
@ -1707,7 +1713,8 @@ void usage(const char* app) {
|
|||
@" -V, --version print the executable version \n"
|
||||
@" -e, --exists check if the app with given bundle_id is installed or not \n"
|
||||
@" -B, --list_bundle_id list bundle_id \n"
|
||||
@" -W, --no-wifi ignore wifi devices\n",
|
||||
@" -W, --no-wifi ignore wifi devices\n"
|
||||
@" --detect_deadlocks <sec> start printing backtraces for all threads periodically after specific amount of seconds\n",
|
||||
[NSString stringWithUTF8String:app]);
|
||||
}
|
||||
|
||||
|
@ -1752,9 +1759,10 @@ int main(int argc, char *argv[]) {
|
|||
{ "exists", no_argument, NULL, 'e'},
|
||||
{ "list_bundle_id", no_argument, NULL, 'B'},
|
||||
{ "no-wifi", no_argument, NULL, 'W'},
|
||||
{ "detect_deadlocks", required_argument, NULL, 1000 },
|
||||
{ NULL, 0, NULL, 0 },
|
||||
};
|
||||
char ch;
|
||||
int ch;
|
||||
|
||||
while ((ch = getopt_long(argc, argv, "VmcdvunrILeD:R:i:b:a:t:g:x:p:1:2:o:l::w::9::B::W", longopts, NULL)) != -1)
|
||||
{
|
||||
|
@ -1855,6 +1863,9 @@ int main(int argc, char *argv[]) {
|
|||
case 'W':
|
||||
no_wifi = true;
|
||||
break;
|
||||
case 1000:
|
||||
_detectDeadlockTimeout = atoi(optarg);
|
||||
break;
|
||||
default:
|
||||
usage(argv[0]);
|
||||
return exitcode_error;
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import lldb
|
||||
import time
|
||||
import os
|
||||
import sys
|
||||
import shlex
|
||||
import lldb
|
||||
|
||||
listener = None
|
||||
|
||||
|
@ -73,6 +74,9 @@ def safequit_command(debugger, command, result, internal_dict):
|
|||
def autoexit_command(debugger, command, result, internal_dict):
|
||||
global listener
|
||||
process = lldb.target.process
|
||||
|
||||
detectDeadlockTimeout = {detect_deadlock_timeout}
|
||||
printBacktraceTime = time.time() + detectDeadlockTimeout if detectDeadlockTimeout > 0 else None
|
||||
|
||||
# This line prevents internal lldb listener from processing STDOUT/STDERR messages. Without it, an order of log writes is incorrect sometimes
|
||||
debugger.GetListener().StopListeningForEvents(process.GetBroadcaster(), lldb.SBProcess.eBroadcastBitSTDOUT | lldb.SBProcess.eBroadcastBitSTDERR )
|
||||
|
@ -113,7 +117,7 @@ def autoexit_command(debugger, command, result, internal_dict):
|
|||
if state == lldb.eStateExited:
|
||||
sys.stdout.write( '\\nPROCESS_EXITED\\n' )
|
||||
os._exit(process.GetExitStatus())
|
||||
elif state == lldb.eStateStopped:
|
||||
elif printBacktraceTime is None and state == lldb.eStateStopped:
|
||||
sys.stdout.write( '\\nPROCESS_STOPPED\\n' )
|
||||
debugger.HandleCommand('bt')
|
||||
os._exit({exitcode_app_crash})
|
||||
|
@ -124,4 +128,10 @@ def autoexit_command(debugger, command, result, internal_dict):
|
|||
elif state == lldb.eStateDetached:
|
||||
sys.stdout.write( '\\nPROCESS_DETACHED\\n' )
|
||||
os._exit({exitcode_app_crash})
|
||||
|
||||
elif printBacktraceTime is not None and time.time() >= printBacktraceTime:
|
||||
printBacktraceTime = None
|
||||
sys.stdout.write( '\\nPRINT_BACKTRACE_TIMEOUT\\n' )
|
||||
debugger.HandleCommand('process interrupt')
|
||||
debugger.HandleCommand('bt all')
|
||||
debugger.HandleCommand('continue')
|
||||
printBacktraceTime = time.time() + 5
|
||||
|
|
Загрузка…
Ссылка в новой задаче