From 8f38048f7d23686a6f008e24cefb45972299d309 Mon Sep 17 00:00:00 2001 From: Tom Cherry Date: Tue, 17 Apr 2018 14:48:44 -0700 Subject: [PATCH] init: add sigstop option for debugging services from their start Test: the examples in README.md Change-Id: Idb528ea4017f8f4ce62911928c040f4bc558d196 --- init/README.md | 50 +++++++++++++++++++++++++++++++++--------------- init/init.cpp | 4 ++++ init/service.cpp | 14 ++++++++++++-- init/service.h | 4 ++++ 4 files changed, 55 insertions(+), 17 deletions(-) diff --git a/init/README.md b/init/README.md index 59ddd777e..b14521cae 100644 --- a/init/README.md +++ b/init/README.md @@ -282,6 +282,10 @@ runs the service. "shutdown critical" will be killed. When the service tagged with "shutdown critical" is not running when shut down starts, it will be started. +`sigstop` +> Send SIGSTOP to the service immediately before exec is called. This is intended for debugging. + See the below section on debugging for how this can be used. + `socket [ [ [ ] ] ]` > Create a unix domain socket named /dev/socket/_name_ and pass its fd to the launched process. _type_ must be "dgram", "stream" or "seqpacket". User and @@ -708,23 +712,39 @@ affected. Debugging init -------------- -By default, programs executed by init will drop stdout and stderr into -/dev/null. To help with debugging, you can execute your program via the -Android program logwrapper. This will redirect stdout/stderr into the -Android logging system (accessed via logcat). +Launching init services without init is not recommended as init sets up a significant amount of +environment (user, groups, security label, capabilities, etc) that is hard to replicate manually. -For example -service akmd /system/bin/logwrapper /sbin/akmd +If it is required to debug a service from its very start, the `sigstop` service option is added. +This option will send SIGSTOP to a service immediately before calling exec. This gives a window +where developers can attach a debugger, strace, etc before continuing the service with SIGCONT. -For quicker turnaround when working on init itself, use: +This flag can also be dynamically controled via the ctl.sigstop_on and ctl.sigstop_off properties. - mm -j && - m ramdisk-nodeps && - m bootimage-nodeps && - adb reboot bootloader && - fastboot boot $ANDROID_PRODUCT_OUT/boot.img +Below is an example of dynamically debugging logd via the above: -Alternatively, use the emulator: + stop logd + setprop ctl.sigstop_on logd + start logd + ps -e | grep logd + > logd 4343 1 18156 1684 do_signal_stop 538280 T init + gdbclient.py -p 4343 + b main + c + c + c + > Breakpoint 1, main (argc=1, argv=0x7ff8c9a488) at system/core/logd/main.cpp:427 - emulator -partition-size 1024 \ - -verbose -show-kernel -no-window +Below is an example of doing the same but with strace + + stop logd + setprop ctl.sigstop_on logd + start logd + ps -e | grep logd + > logd 4343 1 18156 1684 do_signal_stop 538280 T init + strace -p 4343 + + (From a different shell) + kill -SIGCONT 4343 + + > strace runs diff --git a/init/init.cpp b/init/init.cpp index 2f3b28ac5..0e019b059 100644 --- a/init/init.cpp +++ b/init/init.cpp @@ -238,6 +238,10 @@ struct ControlMessageFunction { static const std::map& get_control_message_map() { // clang-format off static const std::map control_message_functions = { + {"sigstop_on", {ControlTarget::SERVICE, + [](auto* service) { service->set_sigstop(true); return Success(); }}}, + {"sigstop_off", {ControlTarget::SERVICE, + [](auto* service) { service->set_sigstop(false); return Success(); }}}, {"start", {ControlTarget::SERVICE, DoControlStart}}, {"stop", {ControlTarget::SERVICE, DoControlStop}}, {"restart", {ControlTarget::SERVICE, DoControlRestart}}, diff --git a/init/service.cpp b/init/service.cpp index 694e5e713..022716152 100644 --- a/init/service.cpp +++ b/init/service.cpp @@ -155,7 +155,7 @@ static void SetUpPidNamespace(const std::string& service_name) { } } -static bool ExpandArgsAndExecv(const std::vector& args) { +static bool ExpandArgsAndExecv(const std::vector& args, bool sigstop) { std::vector expanded_args; std::vector c_strings; @@ -169,6 +169,10 @@ static bool ExpandArgsAndExecv(const std::vector& args) { } c_strings.push_back(nullptr); + if (sigstop) { + kill(getpid(), SIGSTOP); + } + return execv(c_strings[0], c_strings.data()) == 0; } @@ -578,6 +582,11 @@ Result Service::ParseSeclabel(const std::vector& args) { return Success(); } +Result Service::ParseSigstop(const std::vector& args) { + sigstop_ = true; + return Success(); +} + Result Service::ParseSetenv(const std::vector& args) { environment_vars_.emplace_back(args[1], args[2]); return Success(); @@ -700,6 +709,7 @@ const Service::OptionParserMap::Map& Service::OptionParserMap::map() const { {"seclabel", {1, 1, &Service::ParseSeclabel}}, {"setenv", {2, 2, &Service::ParseSetenv}}, {"shutdown", {1, 1, &Service::ParseShutdown}}, + {"sigstop", {0, 0, &Service::ParseSigstop}}, {"socket", {3, 6, &Service::ParseSocket}}, {"user", {1, 1, &Service::ParseUser}}, {"writepid", {1, kMax, &Service::ParseWritepid}}, @@ -858,7 +868,7 @@ Result Service::Start() { // priority. Aborts on failure. SetProcessAttributes(); - if (!ExpandArgsAndExecv(args_)) { + if (!ExpandArgsAndExecv(args_, sigstop_)) { PLOG(ERROR) << "cannot execve('" << args_[0] << "')"; } diff --git a/init/service.h b/init/service.h index d46a41349..9b916c89e 100644 --- a/init/service.h +++ b/init/service.h @@ -114,6 +114,7 @@ class Service { bool is_override() const { return override_; } bool process_cgroup_empty() const { return process_cgroup_empty_; } unsigned long start_order() const { return start_order_; } + void set_sigstop(bool value) { sigstop_ = value; } const std::vector& args() const { return args_; } private: @@ -149,6 +150,7 @@ class Service { Result ParseSeclabel(const std::vector& args); Result ParseSetenv(const std::vector& args); Result ParseShutdown(const std::vector& args); + Result ParseSigstop(const std::vector& args); Result ParseSocket(const std::vector& args); Result ParseFile(const std::vector& args); Result ParseUser(const std::vector& args); @@ -209,6 +211,8 @@ class Service { std::vector> rlimits_; + bool sigstop_ = false; + std::vector args_; };