Merge "init: add sigstop option for debugging services from their start"

This commit is contained in:
Tom Cherry 2018-04-18 13:51:15 +00:00 committed by Gerrit Code Review
commit b41879111d
4 changed files with 55 additions and 17 deletions

View file

@ -282,6 +282,10 @@ runs the service.
"shutdown critical" will be killed. When the service tagged with "shutdown critical" "shutdown critical" will be killed. When the service tagged with "shutdown critical"
is not running when shut down starts, it will be started. 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 <name> <type> <perm> [ <user> [ <group> [ <seclabel> ] ] ]` `socket <name> <type> <perm> [ <user> [ <group> [ <seclabel> ] ] ]`
> Create a unix domain socket named /dev/socket/_name_ and pass its fd to the > 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 launched process. _type_ must be "dgram", "stream" or "seqpacket". User and
@ -708,23 +712,39 @@ affected.
Debugging init Debugging init
-------------- --------------
By default, programs executed by init will drop stdout and stderr into Launching init services without init is not recommended as init sets up a significant amount of
/dev/null. To help with debugging, you can execute your program via the environment (user, groups, security label, capabilities, etc) that is hard to replicate manually.
Android program logwrapper. This will redirect stdout/stderr into the
Android logging system (accessed via logcat).
For example If it is required to debug a service from its very start, the `sigstop` service option is added.
service akmd /system/bin/logwrapper /sbin/akmd 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 && Below is an example of dynamically debugging logd via the above:
m ramdisk-nodeps &&
m bootimage-nodeps &&
adb reboot bootloader &&
fastboot boot $ANDROID_PRODUCT_OUT/boot.img
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 \ Below is an example of doing the same but with strace
-verbose -show-kernel -no-window
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

View file

@ -238,6 +238,10 @@ struct ControlMessageFunction {
static const std::map<std::string, ControlMessageFunction>& get_control_message_map() { static const std::map<std::string, ControlMessageFunction>& get_control_message_map() {
// clang-format off // clang-format off
static const std::map<std::string, ControlMessageFunction> control_message_functions = { static const std::map<std::string, ControlMessageFunction> 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}}, {"start", {ControlTarget::SERVICE, DoControlStart}},
{"stop", {ControlTarget::SERVICE, DoControlStop}}, {"stop", {ControlTarget::SERVICE, DoControlStop}},
{"restart", {ControlTarget::SERVICE, DoControlRestart}}, {"restart", {ControlTarget::SERVICE, DoControlRestart}},

View file

@ -155,7 +155,7 @@ static void SetUpPidNamespace(const std::string& service_name) {
} }
} }
static bool ExpandArgsAndExecv(const std::vector<std::string>& args) { static bool ExpandArgsAndExecv(const std::vector<std::string>& args, bool sigstop) {
std::vector<std::string> expanded_args; std::vector<std::string> expanded_args;
std::vector<char*> c_strings; std::vector<char*> c_strings;
@ -169,6 +169,10 @@ static bool ExpandArgsAndExecv(const std::vector<std::string>& args) {
} }
c_strings.push_back(nullptr); c_strings.push_back(nullptr);
if (sigstop) {
kill(getpid(), SIGSTOP);
}
return execv(c_strings[0], c_strings.data()) == 0; return execv(c_strings[0], c_strings.data()) == 0;
} }
@ -582,6 +586,11 @@ Result<Success> Service::ParseSeclabel(const std::vector<std::string>& args) {
return Success(); return Success();
} }
Result<Success> Service::ParseSigstop(const std::vector<std::string>& args) {
sigstop_ = true;
return Success();
}
Result<Success> Service::ParseSetenv(const std::vector<std::string>& args) { Result<Success> Service::ParseSetenv(const std::vector<std::string>& args) {
environment_vars_.emplace_back(args[1], args[2]); environment_vars_.emplace_back(args[1], args[2]);
return Success(); return Success();
@ -704,6 +713,7 @@ const Service::OptionParserMap::Map& Service::OptionParserMap::map() const {
{"seclabel", {1, 1, &Service::ParseSeclabel}}, {"seclabel", {1, 1, &Service::ParseSeclabel}},
{"setenv", {2, 2, &Service::ParseSetenv}}, {"setenv", {2, 2, &Service::ParseSetenv}},
{"shutdown", {1, 1, &Service::ParseShutdown}}, {"shutdown", {1, 1, &Service::ParseShutdown}},
{"sigstop", {0, 0, &Service::ParseSigstop}},
{"socket", {3, 6, &Service::ParseSocket}}, {"socket", {3, 6, &Service::ParseSocket}},
{"user", {1, 1, &Service::ParseUser}}, {"user", {1, 1, &Service::ParseUser}},
{"writepid", {1, kMax, &Service::ParseWritepid}}, {"writepid", {1, kMax, &Service::ParseWritepid}},
@ -862,7 +872,7 @@ Result<Success> Service::Start() {
// priority. Aborts on failure. // priority. Aborts on failure.
SetProcessAttributes(); SetProcessAttributes();
if (!ExpandArgsAndExecv(args_)) { if (!ExpandArgsAndExecv(args_, sigstop_)) {
PLOG(ERROR) << "cannot execve('" << args_[0] << "')"; PLOG(ERROR) << "cannot execve('" << args_[0] << "')";
} }

View file

@ -118,6 +118,7 @@ class Service {
bool is_override() const { return override_; } bool is_override() const { return override_; }
bool process_cgroup_empty() const { return process_cgroup_empty_; } bool process_cgroup_empty() const { return process_cgroup_empty_; }
unsigned long start_order() const { return start_order_; } unsigned long start_order() const { return start_order_; }
void set_sigstop(bool value) { sigstop_ = value; }
const std::vector<std::string>& args() const { return args_; } const std::vector<std::string>& args() const { return args_; }
private: private:
@ -153,6 +154,7 @@ class Service {
Result<Success> ParseSeclabel(const std::vector<std::string>& args); Result<Success> ParseSeclabel(const std::vector<std::string>& args);
Result<Success> ParseSetenv(const std::vector<std::string>& args); Result<Success> ParseSetenv(const std::vector<std::string>& args);
Result<Success> ParseShutdown(const std::vector<std::string>& args); Result<Success> ParseShutdown(const std::vector<std::string>& args);
Result<Success> ParseSigstop(const std::vector<std::string>& args);
Result<Success> ParseSocket(const std::vector<std::string>& args); Result<Success> ParseSocket(const std::vector<std::string>& args);
Result<Success> ParseFile(const std::vector<std::string>& args); Result<Success> ParseFile(const std::vector<std::string>& args);
Result<Success> ParseUser(const std::vector<std::string>& args); Result<Success> ParseUser(const std::vector<std::string>& args);
@ -213,6 +215,8 @@ class Service {
std::vector<std::pair<int, rlimit>> rlimits_; std::vector<std::pair<int, rlimit>> rlimits_;
bool sigstop_ = false;
std::vector<std::string> args_; std::vector<std::string> args_;
std::vector<std::function<void(const siginfo_t& siginfo)>> reap_callbacks_; std::vector<std::function<void(const siginfo_t& siginfo)>> reap_callbacks_;