Merge pull request #304 from black-square/feature/detect_deadlocks-option
Added an option `--detect_deadlocks`
diff --git a/README.md b/README.md
index 77a6899..6ad87c9 100644
--- a/README.md
+++ b/README.md
@@ -92,6 +92,7 @@
-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 @@
* `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.
diff --git a/src/ios-deploy/ios-deploy.m b/src/ios-deploy/ios-deploy.m
index c890eea..deda5cc 100644
--- a/src/ios-deploy/ios-deploy.m
+++ b/src/ios-deploy/ios-deploy.m
@@ -88,6 +88,7 @@
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 @@
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 @@
@" -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 @@
{ "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 @@
case 'W':
no_wifi = true;
break;
+ case 1000:
+ _detectDeadlockTimeout = atoi(optarg);
+ break;
default:
usage(argv[0]);
return exitcode_error;
diff --git a/src/scripts/lldb.py b/src/scripts/lldb.py
index de297d0..3e149aa 100644
--- a/src/scripts/lldb.py
+++ b/src/scripts/lldb.py
@@ -1,7 +1,8 @@
-import lldb
+import time
import os
import sys
import shlex
+import lldb
listener = None
@@ -78,6 +79,9 @@
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 )
@@ -118,7 +122,7 @@
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})
@@ -129,4 +133,10 @@
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