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

am: f0d28c85b6

Change-Id: If41c3cbbe761642d4138f8a0670191326c66b449
This commit is contained in:
Tom Cherry 2018-04-18 07:03:36 -07:00 committed by android-build-merger
commit 8ee01a9d76
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"
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> ] ] ]`
> 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

View file

@ -238,6 +238,10 @@ struct ControlMessageFunction {
static const std::map<std::string, ControlMessageFunction>& get_control_message_map() {
// clang-format off
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}},
{"stop", {ControlTarget::SERVICE, DoControlStop}},
{"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<char*> c_strings;
@ -169,6 +169,10 @@ static bool ExpandArgsAndExecv(const std::vector<std::string>& 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<Success> Service::ParseSeclabel(const std::vector<std::string>& args) {
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) {
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<Success> Service::Start() {
// priority. Aborts on failure.
SetProcessAttributes();
if (!ExpandArgsAndExecv(args_)) {
if (!ExpandArgsAndExecv(args_, sigstop_)) {
PLOG(ERROR) << "cannot execve('" << args_[0] << "')";
}

View file

@ -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<std::string>& args() const { return args_; }
private:
@ -153,6 +154,7 @@ class Service {
Result<Success> ParseSeclabel(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> ParseSigstop(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> ParseUser(const std::vector<std::string>& args);
@ -213,6 +215,8 @@ class Service {
std::vector<std::pair<int, rlimit>> rlimits_;
bool sigstop_ = false;
std::vector<std::string> args_;
std::vector<std::function<void(const siginfo_t& siginfo)>> reap_callbacks_;