static void wait_for_user_action(const debugger_request_t& request) {
  // Explain how to attach the debugger.
  ALOGI("***********************************************************\n"
        "* Process %d has been suspended while crashing.\n"
        "* To attach gdbserver and start gdb, run this on the host:\n"
        "*\n"
        "*     gdbclient %d\n"
        "*\n"
        "* Wait for gdb to start, then press the VOLUME DOWN key\n"
        "* to let the process continue crashing.\n"
        "***********************************************************",
        request.pid, request.tid);

  // Wait for VOLUME DOWN.
  if (init_getevent() == 0) {
    while (true) {
      input_event e;
      if (get_event(&e, -1) == 0) {
        if (e.type == EV_KEY && e.code == KEY_VOLUMEDOWN && e.value == 0) {
          break;
        }
      }
    }
    uninit_getevent();
  }

  ALOGI("debuggerd resuming process %d", request.pid);
}
예제 #2
0
static void wait_for_user_action(pid_t pid) {
    // First log a helpful message
    LOG(    "********************************************************\n"
            "* Process %d has been suspended while crashing.  To\n"
            "* attach gdbserver for a gdb connection on port 5039\n"
            "* and start gdbclient:\n"
            "*\n"
            "*     gdbclient app_process :5039 %d\n"
            "*\n"
            "* Wait for gdb to start, then press HOME or VOLUME DOWN key\n"
            "* to let the process continue crashing.\n"
            "********************************************************\n",
            pid, pid);

    // wait for HOME or VOLUME DOWN key
    if (init_getevent() == 0) {
        int ms = 1200 / 10;
        int dit = 1;
        int dah = 3*dit;
        int _       = -dit;
        int ___     = 3*_;
        int _______ = 7*_;
        const int codes[] = {
            dit,_,dit,_,dit,___,dah,_,dah,_,dah,___,dit,_,dit,_,dit,_______
        };
        size_t s = 0;
        input_event e;
        bool done = false;
        init_debug_led();
        enable_debug_led();
        do {
            int timeout = abs(codes[s]) * ms;
            int res = get_event(&e, timeout);
            if (res == 0) {
                if (e.type == EV_KEY
                        && (e.code == KEY_HOME || e.code == KEY_VOLUMEDOWN)
                        && e.value == 0) {
                    done = true;
                }
            } else if (res == 1) {
                if (++s >= sizeof(codes)/sizeof(*codes))
                    s = 0;
                if (codes[s] > 0) {
                    enable_debug_led();
                } else {
                    disable_debug_led();
                }
            }
        } while (!done);
        uninit_getevent();
    }

    // don't forget to turn debug led off
    disable_debug_led();
    LOG("debuggerd resuming process %d", pid);
}
예제 #3
0
static void wait_for_user_action(unsigned tid, struct ucred* cr)
{
    (void)tid;
    /* First log a helpful message */
    LOG(    "********************************************************\n"
            "* Process %d has been suspended while crashing.  To\n"
            "* attach gdbserver for a gdb connection on port 5039:\n"
            "*\n"
            "*     adb shell gdbserver :5039 --attach %d &\n"
            "*\n"
            "* Press HOME key to let the process continue crashing.\n"
            "********************************************************\n",
            cr->pid, cr->pid);

    /* wait for HOME key (TODO: something useful for devices w/o HOME key) */
    if (init_getevent() == 0) {
        int ms = 1200 / 10;
        int dit = 1;
        int dah = 3*dit;
        int _       = -dit;
        int ___     = 3*_;
        int _______ = 7*_;
        const signed char codes[] = {
           dit,_,dit,_,dit,___,dah,_,dah,_,dah,___,dit,_,dit,_,dit,_______
        };
        size_t s = 0;
        struct input_event e;
        int home = 0;
        init_debug_led();
        enable_debug_led();
        do {
            int timeout = abs((int)(codes[s])) * ms;
            int res = get_event(&e, timeout);
            if (res == 0) {
                if (e.type==EV_KEY && e.code==KEY_HOME && e.value==0)
                    home = 1;
            } else if (res == 1) {
                if (++s >= sizeof(codes)/sizeof(*codes))
                    s = 0;
                if (codes[s] > 0) {
                    enable_debug_led();
                } else {
                    disable_debug_led();
                }
            }
        } while (!home);
        uninit_getevent();
    }

    /* don't forget to turn debug led off */
    disable_debug_led();

    /* close filedescriptor */
    LOG("debuggerd resuming process %d", cr->pid);
 }
예제 #4
0
static void wait_for_user_action(unsigned tid, struct ucred* cr)
{
    (void)tid;
    /* First log a helpful message */
    LOG(    "********************************************************\n"
            "* process %d crashed. debuggerd waiting for gdbserver   \n"
            "*                                                       \n"
            "*     adb shell gdbserver :port --attach %d &           \n"
            "*                                                       \n"
            "* and press the HOME key.                               \n"
            "********************************************************\n",                
            cr->pid, cr->pid);

    /* wait for HOME key */
    if (init_getevent() == 0) {
        int ms = 1200 / 10;
        int dit = 1;
        int dah = 3*dit;
        int _       = -dit;
        int ___     = 3*_;
        int _______ = 7*_;
        const signed char codes[] = {
           dit,_,dit,_,dit,___,dah,_,dah,_,dah,___,dit,_,dit,_,dit,_______
        };
        size_t s = 0;
        struct input_event e;
        int home = 0;
        init_debug_led();
        enable_debug_led();
        do {
            int timeout = abs((int)(codes[s])) * ms;
            int res = get_event(&e, timeout);
            if (res == 0) {
                if (e.type==EV_KEY && e.code==KEY_HOME && e.value==0)
                    home = 1;
            } else if (res == 1) {
                if (++s >= sizeof(codes)/sizeof(*codes))
                    s = 0;
                if (codes[s] > 0) {
                    enable_debug_led();
                } else {
                    disable_debug_led();
                }
            }
        } while (!home); 
        uninit_getevent();
    }

    /* don't forget to turn debug led off */
    disable_debug_led();
    
    /* close filedescriptor */
    LOG("debuggerd resuming process %d", cr->pid);
 }
예제 #5
0
static void wait_for_user_action(const debugger_request_t &request) {
  // Find out the name of the process that crashed.
  char path[64];
  snprintf(path, sizeof(path), "/proc/%d/exe", request.pid);

  char exe[PATH_MAX];
  int count;
  if ((count = readlink(path, exe, sizeof(exe) - 1)) == -1) {
    ALOGE("readlink('%s') failed: %s", path, strerror(errno));
    strlcpy(exe, "unknown", sizeof(exe));
  } else {
    exe[count] = '\0';
  }

  // Explain how to attach the debugger.
  ALOGI("********************************************************\n"
        "* Process %d has been suspended while crashing.\n"
        "* To attach gdbserver for a gdb connection on port 5039\n"
        "* and start gdbclient:\n"
        "*\n"
        "*     gdbclient %s :5039 %d\n"
        "*\n"
        "* Wait for gdb to start, then press the VOLUME DOWN key\n"
        "* to let the process continue crashing.\n"
        "********************************************************\n",
        request.pid, exe, request.tid);

  // Wait for VOLUME DOWN.
  if (init_getevent() == 0) {
    while (true) {
      input_event e;
      if (get_event(&e, -1) == 0) {
        if (e.type == EV_KEY && e.code == KEY_VOLUMEDOWN && e.value == 0) {
          break;
        }
      }
    }
    uninit_getevent();
  }

  ALOGI("debuggerd resuming process %d", request.pid);
}
예제 #6
0
static void worker_process(int fd, debugger_request_t& request) {
  // Open the tombstone file if we need it.
  std::string tombstone_path;
  int tombstone_fd = -1;
  switch (request.action) {
    case DEBUGGER_ACTION_DUMP_TOMBSTONE:
    case DEBUGGER_ACTION_CRASH:
      tombstone_fd = open_tombstone(&tombstone_path);
      if (tombstone_fd == -1) {
        ALOGE("debuggerd: failed to open tombstone file: %s\n", strerror(errno));
        exit(1);
      }
      break;

    case DEBUGGER_ACTION_DUMP_BACKTRACE:
      break;

    default:
      ALOGE("debuggerd: unexpected request action: %d", request.action);
      exit(1);
  }

  // At this point, the thread that made the request is blocked in
  // a read() call.  If the thread has crashed, then this gives us
  // time to PTRACE_ATTACH to it before it has a chance to really fault.
  //
  // The PTRACE_ATTACH sends a SIGSTOP to the target process, but it
  // won't necessarily have stopped by the time ptrace() returns.  (We
  // currently assume it does.)  We write to the file descriptor to
  // ensure that it can run as soon as we call PTRACE_CONT below.
  // See details in bionic/libc/linker/debugger.c, in function
  // debugger_signal_handler().

  // Attach to the target process.
  if (ptrace(PTRACE_ATTACH, request.tid, 0, 0) != 0) {
    ALOGE("debuggerd: ptrace attach failed: %s", strerror(errno));
    exit(1);
  }

  // Don't attach to the sibling threads if we want to attach gdb.
  // Supposedly, it makes the process less reliable.
  bool attach_gdb = should_attach_gdb(request);
  if (attach_gdb) {
    // Open all of the input devices we need to listen for VOLUMEDOWN before dropping privileges.
    if (init_getevent() != 0) {
      ALOGE("debuggerd: failed to initialize input device, not waiting for gdb");
      attach_gdb = false;
    }

  }

  std::set<pid_t> siblings;
  if (!attach_gdb) {
    ptrace_siblings(request.pid, request.tid, siblings);
  }

  // Generate the backtrace map before dropping privileges.
  std::unique_ptr<BacktraceMap> backtrace_map(BacktraceMap::Create(request.pid));

  bool succeeded = false;

  // Now that we've done everything that requires privileges, we can drop them.
  if (!drop_privileges()) {
    ALOGE("debuggerd: failed to drop privileges, exiting");
    _exit(1);
  }

  int crash_signal = SIGKILL;
  succeeded = perform_dump(request, fd, tombstone_fd, backtrace_map.get(), siblings, &crash_signal);
  if (succeeded) {
    if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
      if (!tombstone_path.empty()) {
        write(fd, tombstone_path.c_str(), tombstone_path.length());
      }
    }
  }

  if (attach_gdb) {
    // Tell the signal process to send SIGSTOP to the target.
    if (!send_signal(request.pid, 0, SIGSTOP)) {
      ALOGE("debuggerd: failed to stop process for gdb attach: %s", strerror(errno));
      attach_gdb = false;
    }
  }

  if (ptrace(PTRACE_DETACH, request.tid, 0, 0) != 0) {
    ALOGE("debuggerd: ptrace detach from %d failed: %s", request.tid, strerror(errno));
  }

  for (pid_t sibling : siblings) {
    ptrace(PTRACE_DETACH, sibling, 0, 0);
  }

  // Send the signal back to the process if it crashed and we're not waiting for gdb.
  if (!attach_gdb && request.action == DEBUGGER_ACTION_CRASH) {
    if (!send_signal(request.pid, request.tid, crash_signal)) {
      ALOGE("debuggerd: failed to kill process %d: %s", request.pid, strerror(errno));
    }
  }

  // Wait for gdb, if requested.
  if (attach_gdb && succeeded) {
    wait_for_user_action(request);

    // Tell the signal process to send SIGCONT to the target.
    if (!send_signal(request.pid, 0, SIGCONT)) {
      ALOGE("debuggerd: failed to resume process %d: %s", request.pid, strerror(errno));
    }

    uninit_getevent();
  }

  exit(!succeeded);
}
예제 #7
0
static void handle_request(int fd) {
  ALOGV("handle_request(%d)\n", fd);

  ScopedFd closer(fd);
  debugger_request_t request;
  memset(&request, 0, sizeof(request));
  int status = read_request(fd, &request);
  if (status != 0) {
    return;
  }

  ALOGV("BOOM: pid=%d uid=%d gid=%d tid=%d\n", request.pid, request.uid, request.gid, request.tid);

#if defined(__LP64__)
  // On 64 bit systems, requests to dump 32 bit and 64 bit tids come
  // to the 64 bit debuggerd. If the process is a 32 bit executable,
  // redirect the request to the 32 bit debuggerd.
  if (is32bit(request.tid)) {
    // Only dump backtrace and dump tombstone requests can be redirected.
    if (request.action == DEBUGGER_ACTION_DUMP_BACKTRACE ||
        request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
      redirect_to_32(fd, &request);
    } else {
      ALOGE("debuggerd: Not allowed to redirect action %d to 32 bit debuggerd\n", request.action);
    }
    return;
  }
#endif

  // Fork a child to handle the rest of the request.
  pid_t fork_pid = fork();
  if (fork_pid == -1) {
    ALOGE("debuggerd: failed to fork: %s\n", strerror(errno));
    return;
  } else if (fork_pid != 0) {
    waitpid(fork_pid, nullptr, 0);
    return;
  }

  // Open the tombstone file if we need it.
  std::string tombstone_path;
  int tombstone_fd = -1;
  switch (request.action) {
    case DEBUGGER_ACTION_DUMP_TOMBSTONE:
    case DEBUGGER_ACTION_CRASH:
      tombstone_fd = open_tombstone(&tombstone_path);
      if (tombstone_fd == -1) {
        ALOGE("debuggerd: failed to open tombstone file: %s\n", strerror(errno));
        exit(1);
      }
      break;

    case DEBUGGER_ACTION_DUMP_BACKTRACE:
      break;

    default:
      ALOGE("debuggerd: unexpected request action: %d", request.action);
      exit(1);
  }

  // At this point, the thread that made the request is blocked in
  // a read() call.  If the thread has crashed, then this gives us
  // time to PTRACE_ATTACH to it before it has a chance to really fault.
  //
  // The PTRACE_ATTACH sends a SIGSTOP to the target process, but it
  // won't necessarily have stopped by the time ptrace() returns.  (We
  // currently assume it does.)  We write to the file descriptor to
  // ensure that it can run as soon as we call PTRACE_CONT below.
  // See details in bionic/libc/linker/debugger.c, in function
  // debugger_signal_handler().

  // Attach to the target process.
  if (ptrace(PTRACE_ATTACH, request.tid, 0, 0) != 0) {
    ALOGE("debuggerd: ptrace attach failed: %s", strerror(errno));
    exit(1);
  }

  // Don't attach to the sibling threads if we want to attach gdb.
  // Supposedly, it makes the process less reliable.
  bool attach_gdb = should_attach_gdb(&request);
  int signal_in_fd = -1;
  int signal_out_fd = -1;
  pid_t signal_pid = 0;
  if (attach_gdb) {
    // Open all of the input devices we need to listen for VOLUMEDOWN before dropping privileges.
    if (init_getevent() != 0) {
      ALOGE("debuggerd: failed to initialize input device, not waiting for gdb");
      attach_gdb = false;
    }

    // Fork a process that stays root, and listens on a pipe to pause and resume the target.
    if (!fork_signal_sender(&signal_in_fd, &signal_out_fd, &signal_pid, request.pid)) {
      attach_gdb = false;
    }
  }

  auto notify_signal_sender = [=]() {
    char buf[1];
    if (TEMP_FAILURE_RETRY(write(signal_in_fd, "", 1)) != 1) {
      ALOGE("debuggerd: failed to notify signal process: %s", strerror(errno));
    } else if (TEMP_FAILURE_RETRY(read(signal_out_fd, buf, 1)) != 1) {
      ALOGE("debuggerd: failed to read response from signal process: %s", strerror(errno));
    }
  };

  std::set<pid_t> siblings;
  if (!attach_gdb) {
    ptrace_siblings(request.pid, request.tid, siblings);
  }

  // Generate the backtrace map before dropping privileges.
  std::unique_ptr<BacktraceMap> backtrace_map(BacktraceMap::Create(request.pid));

  bool succeeded = false;

  // Now that we've done everything that requires privileges, we can drop them.
  if (drop_privileges()) {
    succeeded = perform_dump(request, fd, tombstone_fd, backtrace_map.get(), siblings);
    if (succeeded) {
      if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
        if (!tombstone_path.empty()) {
          write(fd, tombstone_path.c_str(), tombstone_path.length());
        }
      }
    }

    if (attach_gdb) {
      // Tell the signal process to send SIGSTOP to the target.
      notify_signal_sender();
    }
  }

  if (ptrace(PTRACE_DETACH, request.tid, 0, 0) != 0) {
    ALOGE("debuggerd: ptrace detach from %d failed: %s", request.tid, strerror(errno));
  }

  for (pid_t sibling : siblings) {
    ptrace(PTRACE_DETACH, sibling, 0, 0);
  }

  // Wait for gdb, if requested.
  if (attach_gdb && succeeded) {
    wait_for_user_action(request);

    // Tell the signal process to send SIGCONT to the target.
    notify_signal_sender();

    uninit_getevent();
    waitpid(signal_pid, nullptr, 0);
  }

  exit(!succeeded);
}