Merge "init: add sigstop option for debugging services from their start" am: b41879111d
am: f0d28c85b6
Change-Id: If41c3cbbe761642d4138f8a0670191326c66b449
This commit is contained in:
commit
8ee01a9d76
4 changed files with 55 additions and 17 deletions
|
@ -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
|
||||
|
|
|
@ -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}},
|
||||
|
|
|
@ -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] << "')";
|
||||
}
|
||||
|
||||
|
|
|
@ -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_;
|
||||
|
|
Loading…
Reference in a new issue