Skip to content

Commit

Permalink
Added support for 'bugreport -z'.
Browse files Browse the repository at this point in the history
Dumpstate now supports zipped bugreport, whose output is more complete
than the flat-file bugreports provided prior to N.

The whole workflow is split in different components:

- adb supports a 'bugreport -z <ZIP_FILE>' option, which calls a
  bugreportz binary.
- bugreportz starts the dumpstatez service.
- dumpstatez starts dumpstate with some flags that opens a socket for
  control (not output).
- Once dumpstate is finished, it prints the bugreport location to
  stdout.
- adb pulls the zip file and renames according to the command-line
  argument.
- bugreport prints a deprecation message.

The reason for a new binary (bugreportz) instead of passing arguments to
bugreport (like -z) is backward compatibility: pre-N versions of
bugreport would ignore such argument and generate a text bugreport,
which is not what adb would be expecting.

BUG: 27653204

Change-Id: I47f6f677eba11d5fb54818ae5a0b3cab069776ee
  • Loading branch information
the-felipeal committed Apr 13, 2016
1 parent 9ffa1a4 commit 2628e9e
Show file tree
Hide file tree
Showing 7 changed files with 162 additions and 6 deletions.
5 changes: 5 additions & 0 deletions cmds/bugreport/bugreport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@
// output. All of the dumpstate output is written to stdout, including
// any errors encountered while reading/writing the output.
int main() {

fprintf(stderr, "=============================================================================\n");
fprintf(stderr, "WARNING: flat bugreports are deprecated, use adb bugreport <zip_file> instead\n");
fprintf(stderr, "=============================================================================\n\n\n");

// Start the dumpstate service.
property_set("ctl.start", "dumpstate");

Expand Down
12 changes: 12 additions & 0 deletions cmds/bugreportz/Android.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_SRC_FILES:= bugreportz.cpp

LOCAL_MODULE:= bugreportz

LOCAL_CFLAGS := -Wall

LOCAL_SHARED_LIBRARIES := libcutils

include $(BUILD_EXECUTABLE)
93 changes: 93 additions & 0 deletions cmds/bugreportz/bugreportz.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include <errno.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

#include <cutils/properties.h>
#include <cutils/sockets.h>

// TODO: code below was copy-and-pasted from bugreport.cpp (except by the timeout value);
// should be reused instead.
int main() {

// Start the dumpstatez service.
property_set("ctl.start", "dumpstatez");

// Socket will not be available until service starts.
int s;
for (int i = 0; i < 20; i++) {
s = socket_local_client("dumpstate", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM);
if (s >= 0)
break;
// Try again in 1 second.
sleep(1);
}

if (s == -1) {
printf("Failed to connect to dumpstatez service: %s\n", strerror(errno));
return 1;
}

// Set a timeout so that if nothing is read in 10 minutes, we'll stop
// reading and quit. No timeout in dumpstate is longer than 60 seconds,
// so this gives lots of leeway in case of unforeseen time outs.
struct timeval tv;
tv.tv_sec = 10 * 60;
tv.tv_usec = 0;
if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) {
printf("WARNING: Cannot set socket timeout: %s\n", strerror(errno));
}

while (1) {
char buffer[65536];
ssize_t bytes_read = TEMP_FAILURE_RETRY(
read(s, buffer, sizeof(buffer)));
if (bytes_read == 0) {
break;
} else if (bytes_read == -1) {
// EAGAIN really means time out, so change the errno.
if (errno == EAGAIN) {
errno = ETIMEDOUT;
}
printf("\nBugreport read terminated abnormally (%s).\n",
strerror(errno));
break;
}

ssize_t bytes_to_send = bytes_read;
ssize_t bytes_written;
do {
bytes_written = TEMP_FAILURE_RETRY(
write(STDOUT_FILENO, buffer + bytes_read - bytes_to_send,
bytes_to_send));
if (bytes_written == -1) {
printf(
"Failed to write data to stdout: read %zd, trying to send %zd (%s)\n",
bytes_read, bytes_to_send, strerror(errno));
return 1;
}
bytes_to_send -= bytes_written;
} while (bytes_written != 0 && bytes_to_send > 0);
}

close(s);
return 0;

}
36 changes: 32 additions & 4 deletions cmds/dumpstate/dumpstate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ static std::set<std::string> mount_points;
void add_mountinfo();
static bool add_zip_entry(const std::string& entry_name, const std::string& entry_path);
static bool add_zip_entry_from_fd(const std::string& entry_name, int fd);
static int control_socket_fd;

#define PSTORE_LAST_KMSG "/sys/fs/pstore/console-ramoops"

Expand Down Expand Up @@ -929,15 +930,17 @@ static void dumpstate(const std::string& screenshot_path, const std::string& ver

static void usage() {
fprintf(stderr,
"usage: dumpstate [-b soundfile] [-e soundfile] [-o file [-d] [-p] "
"[-z]] [-s] [-q] [-B] [-P] [-R] [-V version]\n"
"usage: dumpstate [-h] [-b soundfile] [-e soundfile] [-o file [-d] [-p] "
"[-z]] [-s] [-S] [-q] [-B] [-P] [-R] [-V version]\n"
" -h: display this help message\n"
" -b: play sound file instead of vibrate, at beginning of job\n"
" -e: play sound file instead of vibrate, at end of job\n"
" -o: write to file (instead of stdout)\n"
" -d: append date to filename (requires -o)\n"
" -p: capture screenshot to filename.png (requires -o)\n"
" -z: generates zipped file (requires -o)\n"
" -z: generate zipped file (requires -o)\n"
" -s: write output to control socket (for init)\n"
" -S: write file location to control socket (for init; requires -o and -z)"
" -q: disable vibrate\n"
" -B: send broadcast when finished (requires -o)\n"
" -P: send broadcast when started and update system properties on "
Expand Down Expand Up @@ -1017,6 +1020,7 @@ int main(int argc, char *argv[]) {
int do_vibrate = 1;
char* use_outfile = 0;
int use_socket = 0;
int use_control_socket = 0;
int do_fb = 0;
int do_broadcast = 0;
int do_early_screenshot = 0;
Expand Down Expand Up @@ -1062,12 +1066,13 @@ int main(int argc, char *argv[]) {
format_args(argc, const_cast<const char **>(argv), &args);
MYLOGD("Dumpstate command line: %s\n", args.c_str());
int c;
while ((c = getopt(argc, argv, "dho:svqzpPBRV:")) != -1) {
while ((c = getopt(argc, argv, "dho:svqzpPBRSV:")) != -1) {
switch (c) {
case 'd': do_add_date = 1; break;
case 'z': do_zip_file = 1; break;
case 'o': use_outfile = optarg; break;
case 's': use_socket = 1; break;
case 'S': use_control_socket = 1; break;
case 'v': break; // compatibility no-op
case 'q': do_vibrate = 0; break;
case 'p': do_fb = 1; break;
Expand All @@ -1087,6 +1092,11 @@ int main(int argc, char *argv[]) {
exit(1);
}

if (use_control_socket && !do_zip_file) {
usage();
exit(1);
}

if (do_update_progress && !do_broadcast) {
usage();
exit(1);
Expand All @@ -1112,6 +1122,11 @@ int main(int argc, char *argv[]) {
redirect_to_socket(stdout, "dumpstate");
}

if (use_control_socket) {
MYLOGD("Opening control socket\n");
control_socket_fd = open_socket("dumpstate");
}

/* full path of the directory where the bugreport files will be written */
std::string bugreport_dir;

Expand Down Expand Up @@ -1351,6 +1366,14 @@ int main(int argc, char *argv[]) {
path.clear();
}
}
if (use_control_socket) {
if (do_text_file) {
dprintf(control_socket_fd, "FAIL:could not create zip file, check %s "
"for more details\n", log_path.c_str());
} else {
dprintf(control_socket_fd, "OK:%s\n", path.c_str());
}
}
}

/* vibrate a few but shortly times to let user know it's finished */
Expand Down Expand Up @@ -1398,5 +1421,10 @@ int main(int argc, char *argv[]) {
fclose(stderr);
}

if (use_control_socket && control_socket_fd >= 0) {
MYLOGD("Closing control socket\n");
close(control_socket_fd);
}

return 0;
}
3 changes: 3 additions & 0 deletions cmds/dumpstate/dumpstate.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,9 @@ void update_progress(int weight);
/* prints all the system properties */
void print_properties();

/** opens a socket and returns its file descriptor */
int open_socket(const char *service);

/* redirect output to a service control socket */
void redirect_to_socket(FILE *redirect, const char *service);

Expand Down
9 changes: 9 additions & 0 deletions cmds/dumpstate/dumpstate.rc
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@ service dumpstate /system/bin/dumpstate -s
disabled
oneshot

# dumpstatez generates a zipped bugreport but also uses a socket to print the file location once
# it is finished.
service dumpstatez /system/bin/dumpstate -S -d -z \
-o /data/user_de/0/com.android.shell/files/bugreports/bugreport
socket dumpstate stream 0660 shell log
class main
disabled
oneshot

# bugreportplus is an enhanced version of bugreport that provides a better
# user interface (like displaying progress and allowing user to enter details).
# It's typically triggered by the power button or developer settings.
Expand Down
10 changes: 8 additions & 2 deletions cmds/dumpstate/utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -909,8 +909,7 @@ void print_properties() {
printf("\n");
}

/* redirect output to a service control socket */
void redirect_to_socket(FILE *redirect, const char *service) {
int open_socket(const char *service) {
int s = android_get_control_socket(service);
if (s < 0) {
MYLOGE("android_get_control_socket(%s): %s\n", service, strerror(errno));
Expand All @@ -930,11 +929,18 @@ void redirect_to_socket(FILE *redirect, const char *service) {
exit(1);
}

return fd;
}

/* redirect output to a service control socket */
void redirect_to_socket(FILE *redirect, const char *service) {
int fd = open_socket(service);
fflush(redirect);
dup2(fd, fileno(redirect));
close(fd);
}

// TODO: should call is_valid_output_file and/or be merged into it.
void create_parent_dirs(const char *path) {
char *chp = const_cast<char *> (path);

Expand Down

0 comments on commit 2628e9e

Please sign in to comment.