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:
Dmitriy Shesterkin 2017-07-26 16:00:32 -03:00
Родитель 35c2c55fd1
Коммит 39449238ae
3 изменённых файлов: 30 добавлений и 5 удалений

Просмотреть файл

@ -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