Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b4187911 authored by Tom Cherry's avatar Tom Cherry Committed by Gerrit Code Review
Browse files

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

parents 01ce44b7 8f38048f
Loading
Loading
Loading
Loading
+40 −20
Original line number Diff line number Diff line
@@ -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).

For example
service akmd /system/bin/logwrapper /sbin/akmd

For quicker turnaround when working on init itself, use:

    mm -j &&
    m ramdisk-nodeps &&
    m bootimage-nodeps &&
    adb reboot bootloader &&
    fastboot boot $ANDROID_PRODUCT_OUT/boot.img

Alternatively, use the emulator:

    emulator -partition-size 1024 \
        -verbose -show-kernel -no-window
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.

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.

This flag can also be dynamically controled via the ctl.sigstop_on and ctl.sigstop_off properties.

Below is an example of dynamically debugging logd via the above:

    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

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
+4 −0
Original line number Diff line number Diff line
@@ -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}},
+12 −2
Original line number Diff line number Diff line
@@ -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] << "')";
        }

+4 −0
Original line number Diff line number Diff line
@@ -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_;