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 40b7e8793..0d5690b07 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 f9986c5f5..03c2ceec8 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; } @@ -582,6 +586,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(); @@ -704,6 +713,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}}, @@ -862,7 +872,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 bcf194386..cf38f69a1 100644 --- a/init/service.h +++ b/init/service.h @@ -118,6 +118,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: @@ -153,6 +154,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); @@ -213,6 +215,8 @@ class Service { std::vector> rlimits_; + bool sigstop_ = false; + std::vector args_; std::vector> reap_callbacks_;