diff options
412 files changed, 16771 insertions, 13105 deletions
diff --git a/CleanSpec.mk b/CleanSpec.mk index 74ec29d..0254bd2 100644 --- a/CleanSpec.mk +++ b/CleanSpec.mk @@ -51,3 +51,5 @@ $(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/init.rc) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/init.rc) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/reboot) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/default.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/default.prop) diff --git a/adb/Android.mk b/adb/Android.mk index 62f012c..50e28a6 100644 --- a/adb/Android.mk +++ b/adb/Android.mk @@ -74,7 +74,7 @@ else LOCAL_SRC_FILES += fdevent.c endif -LOCAL_CFLAGS += -O2 -g -DADB_HOST=1 -Wall -Wno-unused-parameter +LOCAL_CFLAGS += -O2 -g -DADB_HOST=1 -Wall -Wno-unused-parameter -Werror LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE LOCAL_MODULE := adb LOCAL_MODULE_TAGS := debug @@ -116,7 +116,7 @@ LOCAL_SRC_FILES := \ remount_service.c \ usb_linux_client.c -LOCAL_CFLAGS := -O2 -g -DADB_HOST=0 -Wall -Wno-unused-parameter +LOCAL_CFLAGS := -O2 -g -DADB_HOST=0 -Wall -Wno-unused-parameter -Werror LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT))) @@ -138,8 +138,6 @@ include $(BUILD_EXECUTABLE) ifneq ($(SDK_ONLY),true) include $(CLEAR_VARS) -LOCAL_LDLIBS := -lrt -ldl -lpthread - LOCAL_SRC_FILES := \ adb.c \ console.c \ @@ -162,8 +160,7 @@ LOCAL_CFLAGS := \ -g \ -DADB_HOST=1 \ -DADB_HOST_ON_TARGET=1 \ - -Wall \ - -Wno-unused-parameter \ + -Wall -Wno-unused-parameter -Werror \ -D_XOPEN_SOURCE \ -D_GNU_SOURCE diff --git a/adb/SERVICES.TXT b/adb/SERVICES.TXT index 7f85dc3..63000f2 100644 --- a/adb/SERVICES.TXT +++ b/adb/SERVICES.TXT @@ -240,3 +240,20 @@ sync: This starts the file synchronisation service, used to implement "adb push" and "adb pull". Since this service is pretty complex, it will be detailed in a companion document named SYNC.TXT + +reverse:<forward-command> + This implements the 'adb reverse' feature, i.e. the ability to reverse + socket connections from a device to the host. <forward-command> is one + of the forwarding commands that are described above, as in: + + list-forward + forward:<local>;<remote> + forward:norebind:<local>;<remote> + killforward-all + killforward:<local> + + Note that in this case, <local> corresponds to the socket on the device + and <remote> corresponds to the socket on the host. + + The output of reverse:list-forward is the same as host:list-forward + except that <serial> will be just 'host'. diff --git a/adb/SYNC.TXT b/adb/SYNC.TXT new file mode 100644 index 0000000..e74d217 --- /dev/null +++ b/adb/SYNC.TXT @@ -0,0 +1,84 @@ +This file tries to document file related requests a client can make +to the ADB server of an adbd daemon. See the OVERVIEW.TXT document +to understand what's going on here. See the SERVICES.TXT to learn more +about the other requests that are possible. + +SYNC SERVICES: + + +Requesting the sync service ("sync:") using the protocol as described in +SERVICES.TXT sets the connection in sync mode. This mode is a binary mode that +differ from the regular adb protocol. The connection stays in sync mode until +explicitly terminated (see below). + +After the initial "sync:" command is sent the server must respond with either +"OKAY" or "FAIL" as per usual. + +In sync mode both the server and the client will frequently use eight-byte +packets to communicate in this document called sync request and sync +responses. The first four bytes is an id and specifies sync request is +represented by four utf-8 characters. The last four bytes is a Little-Endian +integer, with various uses. This number will be called "length" below. In fact +all binary integers are Little-Endian in the sync mode. Sync mode is +implicitly exited after each sync request, and normal adb communication +follows as described in SERVICES.TXT. + +The following sync requests are accepted: +LIST - List the files in a folder +SEND - Send a file to device +RECV - Retreive a file from device + +Not yet documented: +STAT - Stat a file +ULNK - Unlink (remove) a file. (Not currently supported) + +For all of the sync request above the must be followed by length number of +bytes containing an utf-8 string with a remote filename. + +LIST: +Lists files in the directory specified by the remote filename. The server will +respond with zero or more directory entries or "dents". + +The directory entries will be returned in the following form +1. A four-byte sync response id beeing "DENT" +2. A four-byte integer representing file mode. +3. A four-byte integer representing file size. +4. A four-byte integer representing last modified time. +5. A four-byte integer representing file name length. +6. length number of bytes containing an utf-8 string representing the file + name. + +When an sync response "DONE" is received the listing is done. + +SEND: +The remote file name is split into two parts separated by the last +comma (","). The first part is the actual path, while the second is a decimal +encoded file mode containing the permissions of the file on device. + +Note that some file types will be deleted before the copying starts, and if +the transfer fails. Some file types will not be deleted, which allows + adb push disk_image /some_block_device +to work. + +After this the actual file is sent in chunks. Each chucks has the following +format. +A sync request with id "DATA" and length equal to the chunk size. After +follows chunk size number of bytes. This is repeated until the file is +transfered. Each chunk must not be larger than 64k. + +When the file is tranfered a sync request "DONE" is sent, where length is set +to the last modified time for the file. The server responds to this last +request (but not to chuck requests) with an "OKAY" sync response (length can +be ignored). + + +RECV: +Retrieves a file from device to a local file. The remote path is the path to +the file that will be returned. Just as for the SEND sync request the file +received is split up into chunks. The sync response id is "DATA" and length is +the chuck size. After follows chunk size number of bytes. This is repeated +until the file is transfered. Each chuck will not be larger than 64k. + +When the file is transfered a sync resopnse "DONE" is retrieved where the +length can be ignored. + @@ -318,6 +318,26 @@ static size_t fill_connect_data(char *buf, size_t bufsize) #endif } +#if !ADB_HOST +static void send_msg_with_header(int fd, const char* msg, size_t msglen) { + char header[5]; + if (msglen > 0xffff) + msglen = 0xffff; + snprintf(header, sizeof(header), "%04x", (unsigned)msglen); + writex(fd, header, 4); + writex(fd, msg, msglen); +} +#endif + +static void send_msg_with_okay(int fd, const char* msg, size_t msglen) { + char header[9]; + if (msglen > 0xffff) + msglen = 0xffff; + snprintf(header, sizeof(header), "OKAY%04x", (unsigned)msglen); + writex(fd, header, 8); + writex(fd, msg, msglen); +} + static void send_connect(atransport *t) { D("Calling send_connect \n"); @@ -1324,29 +1344,29 @@ int adb_main(int is_daemon, int server_port) " unchanged.\n"); } + /* add extra groups: + ** AID_ADB to access the USB driver + ** AID_LOG to read system logs (adb logcat) + ** AID_INPUT to diagnose input issues (getevent) + ** AID_INET to diagnose network issues (netcfg, ping) + ** AID_GRAPHICS to access the frame buffer + ** AID_NET_BT and AID_NET_BT_ADMIN to diagnose bluetooth (hcidump) + ** AID_SDCARD_R to allow reading from the SD card + ** AID_SDCARD_RW to allow writing to the SD card + ** AID_NET_BW_STATS to read out qtaguid statistics + */ + gid_t groups[] = { AID_ADB, AID_LOG, AID_INPUT, AID_INET, AID_GRAPHICS, + AID_NET_BT, AID_NET_BT_ADMIN, AID_SDCARD_R, AID_SDCARD_RW, + AID_NET_BW_STATS }; + if (setgroups(sizeof(groups)/sizeof(groups[0]), groups) != 0) { + exit(1); + } + /* don't listen on a port (default 5037) if running in secure mode */ /* don't run as root if we are running in secure mode */ if (should_drop_privileges()) { drop_capabilities_bounding_set_if_needed(); - /* add extra groups: - ** AID_ADB to access the USB driver - ** AID_LOG to read system logs (adb logcat) - ** AID_INPUT to diagnose input issues (getevent) - ** AID_INET to diagnose network issues (netcfg, ping) - ** AID_GRAPHICS to access the frame buffer - ** AID_NET_BT and AID_NET_BT_ADMIN to diagnose bluetooth (hcidump) - ** AID_SDCARD_R to allow reading from the SD card - ** AID_SDCARD_RW to allow writing to the SD card - ** AID_NET_BW_STATS to read out qtaguid statistics - */ - gid_t groups[] = { AID_ADB, AID_LOG, AID_INPUT, AID_INET, AID_GRAPHICS, - AID_NET_BT, AID_NET_BT_ADMIN, AID_SDCARD_R, AID_SDCARD_RW, - AID_NET_BW_STATS }; - if (setgroups(sizeof(groups)/sizeof(groups[0]), groups) != 0) { - exit(1); - } - /* then switch user and group to "shell" */ if (setgid(AID_SHELL) != 0) { exit(1); @@ -1418,10 +1438,123 @@ int adb_main(int is_daemon, int server_port) return 0; } +// Try to handle a network forwarding request. +// This returns 1 on success, 0 on failure, and -1 to indicate this is not +// a forwarding-related request. +int handle_forward_request(const char* service, transport_type ttype, char* serial, int reply_fd) +{ + if (!strcmp(service, "list-forward")) { + // Create the list of forward redirections. + int buffer_size = format_listeners(NULL, 0); + // Add one byte for the trailing zero. + char* buffer = malloc(buffer_size + 1); + if (buffer == NULL) { + sendfailmsg(reply_fd, "not enough memory"); + return 1; + } + (void) format_listeners(buffer, buffer_size + 1); +#if ADB_HOST + send_msg_with_okay(reply_fd, buffer, buffer_size); +#else + send_msg_with_header(reply_fd, buffer, buffer_size); +#endif + free(buffer); + return 1; + } + + if (!strcmp(service, "killforward-all")) { + remove_all_listeners(); +#if ADB_HOST + /* On the host: 1st OKAY is connect, 2nd OKAY is status */ + adb_write(reply_fd, "OKAY", 4); +#endif + adb_write(reply_fd, "OKAY", 4); + return 1; + } + + if (!strncmp(service, "forward:",8) || + !strncmp(service, "killforward:",12)) { + char *local, *remote, *err; + int r; + atransport *transport; + + int createForward = strncmp(service, "kill", 4); + int no_rebind = 0; + + local = strchr(service, ':') + 1; + + // Handle forward:norebind:<local>... here + if (createForward && !strncmp(local, "norebind:", 9)) { + no_rebind = 1; + local = strchr(local, ':') + 1; + } + + remote = strchr(local,';'); + + if (createForward) { + // Check forward: parameter format: '<local>;<remote>' + if(remote == 0) { + sendfailmsg(reply_fd, "malformed forward spec"); + return 1; + } + + *remote++ = 0; + if((local[0] == 0) || (remote[0] == 0) || (remote[0] == '*')) { + sendfailmsg(reply_fd, "malformed forward spec"); + return 1; + } + } else { + // Check killforward: parameter format: '<local>' + if (local[0] == 0) { + sendfailmsg(reply_fd, "malformed forward spec"); + return 1; + } + } + + transport = acquire_one_transport(CS_ANY, ttype, serial, &err); + if (!transport) { + sendfailmsg(reply_fd, err); + return 1; + } + + if (createForward) { + r = install_listener(local, remote, transport, no_rebind); + } else { + r = remove_listener(local, transport); + } + if(r == 0) { +#if ADB_HOST + /* On the host: 1st OKAY is connect, 2nd OKAY is status */ + writex(reply_fd, "OKAY", 4); +#endif + writex(reply_fd, "OKAY", 4); + return 1; + } + + if (createForward) { + const char* message; + switch (r) { + case INSTALL_STATUS_CANNOT_BIND: + message = "cannot bind to socket"; + break; + case INSTALL_STATUS_CANNOT_REBIND: + message = "cannot rebind existing socket"; + break; + default: + message = "internal error"; + } + sendfailmsg(reply_fd, message); + } else { + sendfailmsg(reply_fd, "cannot remove listener"); + } + return 1; + } + return 0; +} + int handle_host_request(char *service, transport_type ttype, char* serial, int reply_fd, asocket *s) { atransport *transport = NULL; - char buf[4096]; if(!strcmp(service, "kill")) { fprintf(stderr,"adb server killed by remote request\n"); @@ -1467,13 +1600,11 @@ int handle_host_request(char *service, transport_type ttype, char* serial, int r char buffer[4096]; int use_long = !strcmp(service+7, "-l"); if (use_long || service[7] == 0) { - memset(buf, 0, sizeof(buf)); memset(buffer, 0, sizeof(buffer)); D("Getting device list \n"); list_transports(buffer, sizeof(buffer), use_long); - snprintf(buf, sizeof(buf), "OKAY%04x%s",(unsigned)strlen(buffer),buffer); D("Wrote device list \n"); - writex(reply_fd, buf, strlen(buf)); + send_msg_with_okay(reply_fd, buffer, strlen(buffer)); return 0; } } @@ -1502,8 +1633,7 @@ int handle_host_request(char *service, transport_type ttype, char* serial, int r } } - snprintf(buf, sizeof(buf), "OKAY%04x%s",(unsigned)strlen(buffer), buffer); - writex(reply_fd, buf, strlen(buf)); + send_msg_with_okay(reply_fd, buffer, strlen(buffer)); return 0; } @@ -1511,8 +1641,7 @@ int handle_host_request(char *service, transport_type ttype, char* serial, int r if (!strcmp(service, "version")) { char version[12]; snprintf(version, sizeof version, "%04x", ADB_SERVER_VERSION); - snprintf(buf, sizeof buf, "OKAY%04x%s", (unsigned)strlen(version), version); - writex(reply_fd, buf, strlen(buf)); + send_msg_with_okay(reply_fd, version, strlen(version)); return 0; } @@ -1522,8 +1651,7 @@ int handle_host_request(char *service, transport_type ttype, char* serial, int r if (transport && transport->serial) { out = transport->serial; } - snprintf(buf, sizeof buf, "OKAY%04x%s",(unsigned)strlen(out),out); - writex(reply_fd, buf, strlen(buf)); + send_msg_with_okay(reply_fd, out, strlen(out)); return 0; } if(!strncmp(service,"get-devpath",strlen("get-devpath"))) { @@ -1532,8 +1660,7 @@ int handle_host_request(char *service, transport_type ttype, char* serial, int r if (transport && transport->devpath) { out = transport->devpath; } - snprintf(buf, sizeof buf, "OKAY%04x%s",(unsigned)strlen(out),out); - writex(reply_fd, buf, strlen(buf)); + send_msg_with_okay(reply_fd, out, strlen(out)); return 0; } // indicates a new emulator instance has started @@ -1545,106 +1672,14 @@ int handle_host_request(char *service, transport_type ttype, char* serial, int r } #endif // ADB_HOST - if(!strcmp(service,"list-forward")) { - // Create the list of forward redirections. - char header[9]; - int buffer_size = format_listeners(NULL, 0); - // Add one byte for the trailing zero. - char* buffer = malloc(buffer_size+1); - (void) format_listeners(buffer, buffer_size+1); - snprintf(header, sizeof header, "OKAY%04x", buffer_size); - writex(reply_fd, header, 8); - writex(reply_fd, buffer, buffer_size); - free(buffer); - return 0; - } - - if (!strcmp(service,"killforward-all")) { - remove_all_listeners(); - adb_write(reply_fd, "OKAYOKAY", 8); - return 0; - } - - if(!strncmp(service,"forward:",8) || - !strncmp(service,"killforward:",12)) { - char *local, *remote, *err; - int r; - atransport *transport; - - int createForward = strncmp(service,"kill",4); - int no_rebind = 0; - - local = strchr(service, ':') + 1; - - // Handle forward:norebind:<local>... here - if (createForward && !strncmp(local, "norebind:", 9)) { - no_rebind = 1; - local = strchr(local, ':') + 1; - } - - remote = strchr(local,';'); - - if (createForward) { - // Check forward: parameter format: '<local>;<remote>' - if(remote == 0) { - sendfailmsg(reply_fd, "malformed forward spec"); - return 0; - } - - *remote++ = 0; - if((local[0] == 0) || (remote[0] == 0) || (remote[0] == '*')){ - sendfailmsg(reply_fd, "malformed forward spec"); - return 0; - } - } else { - // Check killforward: parameter format: '<local>' - if (local[0] == 0) { - sendfailmsg(reply_fd, "malformed forward spec"); - return 0; - } - } - - transport = acquire_one_transport(CS_ANY, ttype, serial, &err); - if (!transport) { - sendfailmsg(reply_fd, err); - return 0; - } - - if (createForward) { - r = install_listener(local, remote, transport, no_rebind); - } else { - r = remove_listener(local, transport); - } - if(r == 0) { - /* 1st OKAY is connect, 2nd OKAY is status */ - writex(reply_fd, "OKAYOKAY", 8); - return 0; - } - - if (createForward) { - const char* message; - switch (r) { - case INSTALL_STATUS_CANNOT_BIND: - message = "cannot bind to socket"; - break; - case INSTALL_STATUS_CANNOT_REBIND: - message = "cannot rebind existing socket"; - break; - default: - message = "internal error"; - } - sendfailmsg(reply_fd, message); - } else { - sendfailmsg(reply_fd, "cannot remove listener"); - } - return 0; - } + int ret = handle_forward_request(service, ttype, serial, reply_fd); + if (ret >= 0) + return ret - 1; if(!strncmp(service,"get-state",strlen("get-state"))) { transport = acquire_one_transport(CS_ANY, ttype, serial, NULL); char *state = connection_state_name(transport); - snprintf(buf, sizeof buf, "OKAY%04x%s",(unsigned)strlen(state),state); - writex(reply_fd, buf, strlen(buf)); + send_msg_with_okay(reply_fd, state, strlen(state)); return 0; } return -1; @@ -323,6 +323,8 @@ asocket* create_jdwp_tracker_service_socket(); int create_jdwp_connection_fd(int jdwp_pid); #endif +int handle_forward_request(const char* service, transport_type ttype, char* serial, int reply_fd); + #if !ADB_HOST typedef enum { BACKUP, diff --git a/adb/adb_auth_host.c b/adb/adb_auth_host.c index 9039d42..783774a 100644 --- a/adb/adb_auth_host.c +++ b/adb/adb_auth_host.c @@ -159,13 +159,13 @@ static int write_public_keyfile(RSA *private_key, const char *private_key_path) bio = BIO_push(b64, bfile); BIO_write(bio, &pkey, sizeof(pkey)); - BIO_flush(bio); + (void) BIO_flush(bio); BIO_pop(b64); BIO_free(b64); get_user_info(info, sizeof(info)); BIO_write(bfile, info, strlen(info)); - BIO_flush(bfile); + (void) BIO_flush(bfile); BIO_free_all(bfile); return 1; diff --git a/adb/adb_client.c b/adb/adb_client.c index 586cd7b..1e47486 100644 --- a/adb/adb_client.c +++ b/adb/adb_client.c @@ -324,7 +324,10 @@ char *adb_query(const char *service) buf[4] = 0; n = strtoul(buf, 0, 16); - if(n > 1024) goto oops; + if(n >= 0xffff) { + strcpy(__adb_error, "reply is too long (>= 64kB)"); + goto oops; + } tmp = malloc(n + 1); if(tmp == 0) goto oops; diff --git a/adb/backup_service.c b/adb/backup_service.c index 669ff86..654e0f3 100644 --- a/adb/backup_service.c +++ b/adb/backup_service.c @@ -52,15 +52,12 @@ int backup_service(BackupOperation op, char* args) { pid_t pid; int s[2]; char* operation; - int socketnum; - // Command string and choice of stdin/stdout for the pipe depend on our invocation + // Command string depends on our invocation if (op == BACKUP) { operation = "backup"; - socketnum = STDOUT_FILENO; } else { operation = "restore"; - socketnum = STDIN_FILENO; } D("backup_service(%s, %s)\n", operation, args); diff --git a/adb/commandline.c b/adb/commandline.c index 61167b2..2e4cd37 100644 --- a/adb/commandline.c +++ b/adb/commandline.c @@ -107,8 +107,12 @@ void help() " will disconnect from all connected TCP/IP devices.\n" "\n" "device commands:\n" - " adb push <local> <remote> - copy file/dir to device\n" - " adb pull <remote> [<local>] - copy file/dir from device\n" + " adb push [-p] <local> <remote>\n" + " - copy file/dir to device\n" + " ('-p' to display the transfer progress)\n" + " adb pull [-p] <remote> [<local>]\n" + " - copy file/dir from device\n" + " ('-p' to display the transfer progress)\n" " adb sync [ <directory> ] - copy host->device only if changed\n" " (-l means list but don't copy)\n" " (see 'adb help all')\n" @@ -132,6 +136,19 @@ void help() " if <local> is already forwarded\n" " adb forward --remove <local> - remove a specific forward socket connection\n" " adb forward --remove-all - remove all forward socket connections\n" + " adb reverse --list - list all reverse socket connections from device\n" + " adb reverse <remote> <local> - reverse socket connections\n" + " reverse specs are one of:\n" + " tcp:<port>\n" + " localabstract:<unix domain socket name>\n" + " localreserved:<unix domain socket name>\n" + " localfilesystem:<unix domain socket name>\n" + " adb reverse --norebind <remote> <local>\n" + " - same as 'adb reverse <remote> <local>' but fails\n" + " if <remote> is already reversed.\n" + " adb reverse --remove <remote>\n" + " - remove a specific reversed socket connection\n" + " adb reverse --remove-all - remove all reversed socket connections from device\n" " adb jdwp - list PIDs of processes hosting a JDWP transport\n" " adb install [-l] [-r] [-d] [-s] [--algo <algorithm name> --key <hex-encoded key> --iv <hex-encoded iv>] <file>\n" " - push this package file to the device and install it\n" @@ -403,7 +420,7 @@ int adb_download_buffer(const char *service, const char *fn, const void* data, i } int opt = CHUNK_SIZE; - opt = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt)); + opt = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (const void *) &opt, sizeof(opt)); total = sz; ptr = data; @@ -678,10 +695,10 @@ static int logcat(transport_type transport, char* serial, int argc, char **argv) return 0; } -static int mkdirs(char *path) +static int mkdirs(const char *path) { int ret; - char *x = path + 1; + char *x = (char *)path + 1; for(;;) { x = adb_dirstart(x); @@ -724,7 +741,7 @@ static int backup(int argc, char** argv) { if (argc < 2) return usage(); adb_unlink(filename); - mkdirs((char *)filename); + mkdirs(filename); outFd = adb_creat(filename, 0640); if (outFd < 0) { fprintf(stderr, "adb: unable to open file %s\n", filename); @@ -922,6 +939,28 @@ static const char *find_product_out_path(const char *hint) return path_buf; } + +static void parse_push_pull_args(char** arg, int narg, char const** path1, char const** path2, + int* show_progress) { + *show_progress = 0; + + if ((narg > 0) && !strcmp(*arg, "-p")) { + *show_progress = 1; + ++arg; + --narg; + } + + if (narg > 0) { + *path1 = *arg; + ++arg; + --narg; + } + + if (narg > 0) { + *path2 = *arg; + } +} + int adb_commandline(int argc, char **argv) { char buf[4096]; @@ -1274,8 +1313,11 @@ top: return 0; } - if(!strcmp(argv[0], "forward")) { + if(!strcmp(argv[0], "forward") || + !strcmp(argv[0], "reverse")) + { char host_prefix[64]; + char reverse = (char) !strcmp(argv[0], "reverse"); char remove = 0; char remove_all = 0; char list = 0; @@ -1304,15 +1346,19 @@ top: } // Determine the <host-prefix> for this command. - if (serial) { - snprintf(host_prefix, sizeof host_prefix, "host-serial:%s", - serial); - } else if (ttype == kTransportUsb) { - snprintf(host_prefix, sizeof host_prefix, "host-usb"); - } else if (ttype == kTransportLocal) { - snprintf(host_prefix, sizeof host_prefix, "host-local"); + if (reverse) { + snprintf(host_prefix, sizeof host_prefix, "reverse"); } else { - snprintf(host_prefix, sizeof host_prefix, "host"); + if (serial) { + snprintf(host_prefix, sizeof host_prefix, "host-serial:%s", + serial); + } else if (ttype == kTransportUsb) { + snprintf(host_prefix, sizeof host_prefix, "host-usb"); + } else if (ttype == kTransportLocal) { + snprintf(host_prefix, sizeof host_prefix, "host-local"); + } else { + snprintf(host_prefix, sizeof host_prefix, "host"); + } } // Implement forward --list @@ -1369,18 +1415,29 @@ top: } if(!strcmp(argv[0], "push")) { - if(argc != 3) return usage(); - return do_sync_push(argv[1], argv[2], 0 /* no verify APK */); + int show_progress = 0; + const char* lpath = NULL, *rpath = NULL; + + parse_push_pull_args(&argv[1], argc - 1, &lpath, &rpath, &show_progress); + + if ((lpath != NULL) && (rpath != NULL)) { + return do_sync_push(lpath, rpath, 0 /* no verify APK */, show_progress); + } + + return usage(); } if(!strcmp(argv[0], "pull")) { - if (argc == 2) { - return do_sync_pull(argv[1], "."); - } else if (argc == 3) { - return do_sync_pull(argv[1], argv[2]); - } else { - return usage(); + int show_progress = 0; + const char* rpath = NULL, *lpath = "."; + + parse_push_pull_args(&argv[1], argc - 1, &rpath, &lpath, &show_progress); + + if (rpath != NULL) { + return do_sync_pull(rpath, lpath, show_progress); } + + return usage(); } if(!strcmp(argv[0], "install")) { @@ -1687,6 +1744,8 @@ int install_app(transport_type transport, char* serial, int argc, char** argv) } else if (!strcmp(argv[i], "--key")) { verify_apk = 0; i++; + } else if (!strcmp(argv[i], "--abi")) { + i++; } } @@ -1718,7 +1777,7 @@ int install_app(transport_type transport, char* serial, int argc, char** argv) } } - err = do_sync_push(apk_file, apk_dest, verify_apk); + err = do_sync_push(apk_file, apk_dest, verify_apk, 0 /* no show progress */); if (err) { goto cleanup_apk; } else { @@ -1726,7 +1785,8 @@ int install_app(transport_type transport, char* serial, int argc, char** argv) } if (verification_file != NULL) { - err = do_sync_push(verification_file, verification_dest, 0 /* no verify APK */); + err = do_sync_push(verification_file, verification_dest, 0 /* no verify APK */, + 0 /* no show progress */); if (err) { goto cleanup_apk; } else { diff --git a/adb/file_sync_client.c b/adb/file_sync_client.c index 9fec081..dc4e77f 100644 --- a/adb/file_sync_client.c +++ b/adb/file_sync_client.c @@ -62,6 +62,22 @@ static void END() total_bytes, (t / 1000000LL), (t % 1000000LL) / 1000LL); } +static const char* transfer_progress_format = "\rTransferring: %llu/%llu (%d%%)"; + +static void print_transfer_progress(unsigned long long bytes_current, + unsigned long long bytes_total) { + if (bytes_total == 0) return; + + fprintf(stderr, transfer_progress_format, bytes_current, bytes_total, + (int) (bytes_current * 100 / bytes_total)); + + if (bytes_current == bytes_total) { + fputc('\n', stderr); + } + + fflush(stderr); +} + void sync_quit(int fd) { syncmsg msg; @@ -207,9 +223,10 @@ int sync_readmode(int fd, const char *path, unsigned *mode) return 0; } -static int write_data_file(int fd, const char *path, syncsendbuf *sbuf) +static int write_data_file(int fd, const char *path, syncsendbuf *sbuf, int show_progress) { int lfd, err = 0; + unsigned long long size = 0; lfd = adb_open(path, O_RDONLY); if(lfd < 0) { @@ -217,6 +234,17 @@ static int write_data_file(int fd, const char *path, syncsendbuf *sbuf) return -1; } + if (show_progress) { + // Determine local file size. + struct stat st; + if (lstat(path, &st)) { + fprintf(stderr,"cannot stat '%s': %s\n", path, strerror(errno)); + return -1; + } + + size = st.st_size; + } + sbuf->id = ID_DATA; for(;;) { int ret; @@ -238,13 +266,18 @@ static int write_data_file(int fd, const char *path, syncsendbuf *sbuf) break; } total_bytes += ret; + + if (show_progress) { + print_transfer_progress(total_bytes, size); + } } adb_close(lfd); return err; } -static int write_data_buffer(int fd, char* file_buffer, int size, syncsendbuf *sbuf) +static int write_data_buffer(int fd, char* file_buffer, int size, syncsendbuf *sbuf, + int show_progress) { int err = 0; int total = 0; @@ -264,6 +297,10 @@ static int write_data_buffer(int fd, char* file_buffer, int size, syncsendbuf *s } total += count; total_bytes += count; + + if (show_progress) { + print_transfer_progress(total, size); + } } return err; @@ -295,7 +332,7 @@ static int write_data_link(int fd, const char *path, syncsendbuf *sbuf) #endif static int sync_send(int fd, const char *lpath, const char *rpath, - unsigned mtime, mode_t mode, int verifyApk) + unsigned mtime, mode_t mode, int verifyApk, int show_progress) { syncmsg msg; int len, r; @@ -377,10 +414,10 @@ static int sync_send(int fd, const char *lpath, const char *rpath, } if (file_buffer) { - write_data_buffer(fd, file_buffer, size, sbuf); + write_data_buffer(fd, file_buffer, size, sbuf, show_progress); free(file_buffer); } else if (S_ISREG(mode)) - write_data_file(fd, lpath, sbuf); + write_data_file(fd, lpath, sbuf, show_progress); #ifdef HAVE_SYMLINKS else if (S_ISLNK(mode)) write_data_link(fd, lpath, sbuf); @@ -419,10 +456,10 @@ fail: return -1; } -static int mkdirs(char *name) +static int mkdirs(const char *name) { int ret; - char *x = name + 1; + char *x = (char *)name + 1; for(;;) { x = adb_dirstart(x); @@ -438,17 +475,38 @@ static int mkdirs(char *name) return 0; } -int sync_recv(int fd, const char *rpath, const char *lpath) +int sync_recv(int fd, const char *rpath, const char *lpath, int show_progress) { syncmsg msg; int len; int lfd = -1; char *buffer = send_buffer.data; unsigned id; + unsigned long long size = 0; len = strlen(rpath); if(len > 1024) return -1; + if (show_progress) { + // Determine remote file size. + syncmsg stat_msg; + stat_msg.req.id = ID_STAT; + stat_msg.req.namelen = htoll(len); + + if (writex(fd, &stat_msg.req, sizeof(stat_msg.req)) || + writex(fd, rpath, len)) { + return -1; + } + + if (readx(fd, &stat_msg.stat, sizeof(stat_msg.stat))) { + return -1; + } + + if (stat_msg.stat.id != ID_STAT) return -1; + + size = ltohl(stat_msg.stat.size); + } + msg.req.id = ID_RECV; msg.req.namelen = htoll(len); if(writex(fd, &msg.req, sizeof(msg.req)) || @@ -463,7 +521,7 @@ int sync_recv(int fd, const char *rpath, const char *lpath) if((id == ID_DATA) || (id == ID_DONE)) { adb_unlink(lpath); - mkdirs((char *)lpath); + mkdirs(lpath); lfd = adb_creat(lpath, 0644); if(lfd < 0) { fprintf(stderr,"cannot create '%s': %s\n", lpath, strerror(errno)); @@ -502,6 +560,10 @@ int sync_recv(int fd, const char *rpath, const char *lpath) } total_bytes += len; + + if (show_progress) { + print_transfer_progress(total_bytes, size); + } } adb_close(lfd); @@ -721,7 +783,8 @@ static int copy_local_dir_remote(int fd, const char *lpath, const char *rpath, i if(ci->flag == 0) { fprintf(stderr,"%spush: %s -> %s\n", listonly ? "would " : "", ci->src, ci->dst); if(!listonly && - sync_send(fd, ci->src, ci->dst, ci->time, ci->mode, 0 /* no verify APK */)){ + sync_send(fd, ci->src, ci->dst, ci->time, ci->mode, 0 /* no verify APK */, + 0 /* no show progress */)) { return 1; } pushed++; @@ -739,7 +802,7 @@ static int copy_local_dir_remote(int fd, const char *lpath, const char *rpath, i } -int do_sync_push(const char *lpath, const char *rpath, int verifyApk) +int do_sync_push(const char *lpath, const char *rpath, int verifyApk, int show_progress) { struct stat st; unsigned mode; @@ -786,7 +849,7 @@ int do_sync_push(const char *lpath, const char *rpath, int verifyApk) rpath = tmp; } BEGIN(); - if(sync_send(fd, lpath, rpath, st.st_mtime, st.st_mode, verifyApk)) { + if(sync_send(fd, lpath, rpath, st.st_mtime, st.st_mode, verifyApk, show_progress)) { return 1; } else { END(); @@ -923,7 +986,7 @@ static int copy_remote_dir_local(int fd, const char *rpath, const char *lpath, next = ci->next; if (ci->flag == 0) { fprintf(stderr, "pull: %s -> %s\n", ci->src, ci->dst); - if (sync_recv(fd, ci->src, ci->dst)) { + if (sync_recv(fd, ci->src, ci->dst, 0 /* no show progress */)) { return 1; } pulled++; @@ -940,7 +1003,7 @@ static int copy_remote_dir_local(int fd, const char *rpath, const char *lpath, return 0; } -int do_sync_pull(const char *rpath, const char *lpath) +int do_sync_pull(const char *rpath, const char *lpath, int show_progress) { unsigned mode; struct stat st; @@ -981,7 +1044,7 @@ int do_sync_pull(const char *rpath, const char *lpath) } } BEGIN(); - if(sync_recv(fd, rpath, lpath)) { + if (sync_recv(fd, rpath, lpath, show_progress)) { return 1; } else { END(); diff --git a/adb/file_sync_service.c b/adb/file_sync_service.c index 577fb8f..1d80d26 100644 --- a/adb/file_sync_service.c +++ b/adb/file_sync_service.c @@ -68,7 +68,7 @@ static int mkdirs(char *name) *x = '/'; return ret; } - selinux_android_restorecon(name); + selinux_android_restorecon(name, 0); } *x++ = '/'; } @@ -172,7 +172,7 @@ static int fail_errno(int s) } static int handle_send_file(int s, char *path, uid_t uid, - gid_t gid, mode_t mode, char *buffer) + gid_t gid, mode_t mode, char *buffer, bool do_unlink) { syncmsg msg; unsigned int timestamp = 0; @@ -236,7 +236,7 @@ static int handle_send_file(int s, char *path, uid_t uid, if(writex(fd, buffer, len)) { int saved_errno = errno; adb_close(fd); - adb_unlink(path); + if (do_unlink) adb_unlink(path); fd = -1; errno = saved_errno; if(fail_errno(s)) return -1; @@ -246,7 +246,7 @@ static int handle_send_file(int s, char *path, uid_t uid, if(fd >= 0) { struct utimbuf u; adb_close(fd); - selinux_android_restorecon(path); + selinux_android_restorecon(path, 0); u.actime = timestamp; u.modtime = timestamp; utime(path, &u); @@ -261,7 +261,7 @@ static int handle_send_file(int s, char *path, uid_t uid, fail: if(fd >= 0) adb_close(fd); - adb_unlink(path); + if (do_unlink) adb_unlink(path); return -1; } @@ -323,6 +323,7 @@ static int do_send(int s, char *path, char *buffer) char *tmp; unsigned int mode; int is_link, ret; + bool do_unlink; tmp = strrchr(path,','); if(tmp) { @@ -339,11 +340,16 @@ static int do_send(int s, char *path, char *buffer) if(!tmp || errno) { mode = 0644; is_link = 0; + do_unlink = true; + } else { + struct stat st; + /* Don't delete files before copying if they are not "regular" */ + do_unlink = lstat(path, &st) || S_ISREG(st.st_mode) || S_ISLNK(st.st_mode); + if (do_unlink) { + adb_unlink(path); + } } - adb_unlink(path); - - #ifdef HAVE_SYMLINKS if(is_link) ret = handle_send_link(s, path, buffer); @@ -366,7 +372,7 @@ static int do_send(int s, char *path, char *buffer) if (is_on_system(path)) { fs_config(tmp, 0, &uid, &gid, &mode, &cap); } - ret = handle_send_file(s, path, uid, gid, mode, buffer); + ret = handle_send_file(s, path, uid, gid, mode, buffer, do_unlink); } return ret; diff --git a/adb/file_sync_service.h b/adb/file_sync_service.h index e402e06..3e7e096 100644 --- a/adb/file_sync_service.h +++ b/adb/file_sync_service.h @@ -72,15 +72,15 @@ typedef union { struct { unsigned id; unsigned msglen; - } status; + } status; } syncmsg; void file_sync_service(int fd, void *cookie); int do_sync_ls(const char *path); -int do_sync_push(const char *lpath, const char *rpath, int verifyApk); +int do_sync_push(const char *lpath, const char *rpath, int verifyApk, int show_progress); int do_sync_sync(const char *lpath, const char *rpath, int listonly); -int do_sync_pull(const char *rpath, const char *lpath); +int do_sync_pull(const char *rpath, const char *lpath, int show_progress); #define SYNC_DATA_MAX (64*1024) diff --git a/adb/framebuffer_service.c b/adb/framebuffer_service.c index fa7fd98..8cbe840 100644 --- a/adb/framebuffer_service.c +++ b/adb/framebuffer_service.c @@ -61,7 +61,7 @@ void framebuffer_service(int fd, void *cookie) int w, h, f; int fds[2]; - if (pipe(fds) < 0) goto pipefail; + if (pipe2(fds, O_CLOEXEC) < 0) goto pipefail; pid_t pid = fork(); if (pid < 0) goto done; diff --git a/adb/remount_service.c b/adb/remount_service.c index ad61284..d3a649b 100644 --- a/adb/remount_service.c +++ b/adb/remount_service.c @@ -14,13 +14,13 @@ * limitations under the License. */ -#include <stdlib.h> +#include <errno.h> +#include <fcntl.h> #include <stdio.h> -#include <unistd.h> +#include <stdlib.h> #include <string.h> -#include <fcntl.h> #include <sys/mount.h> -#include <errno.h> +#include <unistd.h> #include "sysdeps.h" @@ -35,7 +35,6 @@ static char *find_mount(const char *dir) { int fd; int res; - int size; char *token = NULL; const char delims[] = "\n"; char buf[4096]; @@ -45,7 +44,7 @@ static char *find_mount(const char *dir) return NULL; buf[sizeof(buf) - 1] = '\0'; - size = adb_read(fd, buf, sizeof(buf) - 1); + adb_read(fd, buf, sizeof(buf) - 1); adb_close(fd); token = strtok(buf, delims); diff --git a/adb/services.c b/adb/services.c index dcaf276..7b809da 100644 --- a/adb/services.c +++ b/adb/services.c @@ -141,6 +141,17 @@ cleanup: adb_close(fd); } +void reverse_service(int fd, void* arg) +{ + const char* command = arg; + + if (handle_forward_request(command, kTransportAny, NULL, fd) < 0) { + sendfailmsg(fd, "not a reverse forwarding command"); + } + free(arg); + adb_close(fd); +} + #endif static int create_service_thread(void (*func)(int, void *), void *cookie) @@ -385,6 +396,16 @@ int service_to_fd(const char *name) ret = create_service_thread(restart_tcp_service, (void *) (uintptr_t) port); } else if(!strncmp(name, "usb:", 4)) { ret = create_service_thread(restart_usb_service, NULL); + } else if (!strncmp(name, "reverse:", 8)) { + char* cookie = strdup(name + 8); + if (cookie == NULL) { + ret = -1; + } else { + ret = create_service_thread(reverse_service, cookie); + if (ret < 0) { + free(cookie); + } + } #endif } if (ret >= 0) { @@ -447,7 +468,7 @@ static void connect_device(char* host, char* buffer, int buffer_size) snprintf(serial, sizeof(serial), "%s:%d", hostbuf, port); - fd = socket_network_client(hostbuf, port, SOCK_STREAM); + fd = socket_network_client_timeout(hostbuf, port, SOCK_STREAM, 10); if (fd < 0) { snprintf(buffer, buffer_size, "unable to connect to %s:%d", host, port); return; diff --git a/adb/sockets.c b/adb/sockets.c index de14a22..faa9564 100644 --- a/adb/sockets.c +++ b/adb/sockets.c @@ -23,6 +23,10 @@ #include "sysdeps.h" +#if !ADB_HOST +#include <cutils/properties.h> +#endif + #define TRACE_TAG TRACE_SOCKETS #include "adb.h" @@ -428,6 +432,9 @@ asocket *create_local_service_socket(const char *name) { asocket *s; int fd; +#if !ADB_HOST + char debug[PROPERTY_VALUE_MAX]; +#endif #if !ADB_HOST if (!strcmp(name,"jdwp")) { @@ -444,7 +451,11 @@ asocket *create_local_service_socket(const char *name) D("LS(%d): bound to '%s' via %d\n", s->id, name, fd); #if !ADB_HOST - if ((!strncmp(name, "root:", 5) && getuid() != 0) + if (!strncmp(name, "root:", 5)) + property_get("ro.debuggable", debug, ""); + + if ((!strncmp(name, "root:", 5) && getuid() != 0 + && strcmp(debug, "1") == 0) || !strncmp(name, "usb:", 4) || !strncmp(name, "tcpip:", 6)) { D("LS(%d): enabling exit_on_close\n", s->id); diff --git a/adb/sysdeps.h b/adb/sysdeps.h index 4033b72..ba4306f 100644 --- a/adb/sysdeps.h +++ b/adb/sysdeps.h @@ -169,6 +169,8 @@ extern void* load_file(const char* pathname, unsigned* psize); /* normally provided by <cutils/sockets.h> */ extern int socket_loopback_client(int port, int type); extern int socket_network_client(const char *host, int port, int type); +extern int socket_network_client_timeout(const char *host, int port, int type, + int timeout); extern int socket_loopback_server(int port, int type); extern int socket_inaddr_any_server(int port, int type); diff --git a/adb/sysdeps_win32.c b/adb/sysdeps_win32.c index 2105b16..29f58ec 100644 --- a/adb/sysdeps_win32.c +++ b/adb/sysdeps_win32.c @@ -701,6 +701,13 @@ int socket_network_client(const char *host, int port, int type) } +int socket_network_client_timeout(const char *host, int port, int type, int timeout) +{ + // TODO: implement timeouts for Windows. + return socket_network_client(host, port, type); +} + + int socket_inaddr_any_server(int port, int type) { FH f = _fh_alloc( &_fh_socket_class ); @@ -956,7 +963,7 @@ bip_buffer_write( BipBuffer bip, const void* src, int len ) avail = len; memcpy( bip->buff + bip->a_end, src, avail ); - src += avail; + src = (const char *)src + avail; count += avail; len -= avail; @@ -1049,7 +1056,7 @@ bip_buffer_read( BipBuffer bip, void* dst, int len ) avail = len; memcpy( dst, bip->buff + bip->a_start, avail ); - dst += avail; + dst = (char *)dst + avail; count += avail; len -= avail; diff --git a/adb/transport.c b/adb/transport.c index 6002530..f35880c 100644 --- a/adb/transport.c +++ b/adb/transport.c @@ -1188,6 +1188,10 @@ int writex(int fd, const void *ptr, size_t len) D("writex: fd=%d error %d: %s\n", fd, errno, strerror(errno)); if (errno == EINTR) continue; + if (errno == EAGAIN) { + adb_sleep_ms(1); // just yield some cpu time + continue; + } } else { D("writex: fd=%d disconnected\n", fd); } diff --git a/adb/transport_local.c b/adb/transport_local.c index d2dbca6..948cc15 100644 --- a/adb/transport_local.c +++ b/adb/transport_local.c @@ -48,7 +48,7 @@ static inline void fix_endians(apacket *p) * local transport it is connected. The list is used to detect when we're * trying to connect twice to a given local transport. */ -#define ADB_LOCAL_TRANSPORT_MAX 16 +#define ADB_LOCAL_TRANSPORT_MAX 64 ADB_MUTEX_DEFINE( local_transports_lock ); diff --git a/adb/usb_vendors.c b/adb/usb_vendors.c index cd5885e..957e5db 100755 --- a/adb/usb_vendors.c +++ b/adb/usb_vendors.c @@ -51,7 +51,9 @@ // BYD's USB Vendor ID #define VENDOR_ID_BYD 0x1D91 // Compal's USB Vendor ID -#define VENDOR_ID_COMPAL 0x1219 +#define VENDOR_ID_COMPAL 0x04B7 +// Compalcomm's USB Vendor ID +#define VENDOR_ID_COMPALCOMM 0x1219 // Dell's USB Vendor ID #define VENDOR_ID_DELL 0x413c // ECS's USB Vendor ID @@ -72,6 +74,8 @@ #define VENDOR_ID_GIGABYTE 0x0414 // Gigaset's USB Vendor ID #define VENDOR_ID_GIGASET 0x1E85 +// GIONEE's USB Vendor ID +#define VENDOR_ID_GIONEE 0x271D // Google's USB Vendor ID #define VENDOR_ID_GOOGLE 0x18d1 // Haier's USB Vendor ID @@ -80,6 +84,8 @@ #define VENDOR_ID_HARRIS 0x19A5 // Hisense's USB Vendor ID #define VENDOR_ID_HISENSE 0x109b +// Honeywell's USB Vendor ID +#define VENDOR_ID_HONEYWELL 0x0c2e // HP's USB Vendor ID #define VENDOR_ID_HP 0x03f0 // HTC's USB Vendor ID @@ -90,6 +96,8 @@ #define VENDOR_ID_INQ_MOBILE 0x2314 // Intel's USB Vendor ID #define VENDOR_ID_INTEL 0x8087 +// Intermec's USB Vendor ID +#define VENDOR_ID_INTERMEC 0x067e // IRiver's USB Vendor ID #define VENDOR_ID_IRIVER 0x2420 // K-Touch's USB Vendor ID @@ -138,6 +146,8 @@ #define VENDOR_ID_PMC 0x04DA // Positivo's USB Vendor ID #define VENDOR_ID_POSITIVO 0x1662 +// Prestigio's USB Vendor ID +#define VENDOR_ID_PRESTIGIO 0x29e4 // Qisda's USB Vendor ID #define VENDOR_ID_QISDA 0x1D45 // Qualcomm's USB Vendor ID @@ -152,6 +162,8 @@ #define VENDOR_ID_SHARP 0x04dd // SK Telesys's USB Vendor ID #define VENDOR_ID_SK_TELESYS 0x1F53 +// Smartisan's USB Vendor ID +#define VENDOR_ID_SMARTISAN 0x29a9 // Sony's USB Vendor ID #define VENDOR_ID_SONY 0x054C // Sony Ericsson's USB Vendor ID @@ -166,6 +178,8 @@ #define VENDOR_ID_TI 0x0451 // Toshiba's USB Vendor ID #define VENDOR_ID_TOSHIBA 0x0930 +// Unowhy's USB Vendor ID +#define VENDOR_ID_UNOWHY 0x2A49 // Vizio's USB Vendor ID #define VENDOR_ID_VIZIO 0xE040 // Wacom's USB Vendor ID @@ -191,6 +205,7 @@ int builtInVendorIds[] = { VENDOR_ID_ASUS, VENDOR_ID_BYD, VENDOR_ID_COMPAL, + VENDOR_ID_COMPALCOMM, VENDOR_ID_DELL, VENDOR_ID_ECS, VENDOR_ID_EMERGING_TECH, @@ -201,15 +216,18 @@ int builtInVendorIds[] = { VENDOR_ID_GARMIN_ASUS, VENDOR_ID_GIGABYTE, VENDOR_ID_GIGASET, + VENDOR_ID_GIONEE, VENDOR_ID_GOOGLE, VENDOR_ID_HAIER, VENDOR_ID_HARRIS, VENDOR_ID_HISENSE, + VENDOR_ID_HONEYWELL, VENDOR_ID_HP, VENDOR_ID_HTC, VENDOR_ID_HUAWEI, VENDOR_ID_INQ_MOBILE, VENDOR_ID_INTEL, + VENDOR_ID_INTERMEC, VENDOR_ID_IRIVER, VENDOR_ID_KOBO, VENDOR_ID_K_TOUCH, @@ -234,6 +252,7 @@ int builtInVendorIds[] = { VENDOR_ID_PHILIPS, VENDOR_ID_PMC, VENDOR_ID_POSITIVO, + VENDOR_ID_PRESTIGIO, VENDOR_ID_QISDA, VENDOR_ID_QUALCOMM, VENDOR_ID_QUANTA, @@ -241,6 +260,7 @@ int builtInVendorIds[] = { VENDOR_ID_SAMSUNG, VENDOR_ID_SHARP, VENDOR_ID_SK_TELESYS, + VENDOR_ID_SMARTISAN, VENDOR_ID_SONY, VENDOR_ID_SONY_ERICSSON, VENDOR_ID_T_AND_A, @@ -248,6 +268,7 @@ int builtInVendorIds[] = { VENDOR_ID_TELEEPOCH, VENDOR_ID_TI, VENDOR_ID_TOSHIBA, + VENDOR_ID_UNOWHY, VENDOR_ID_VIZIO, VENDOR_ID_WACOM, VENDOR_ID_XIAOMI, diff --git a/adb/usb_windows.c b/adb/usb_windows.c index 4936b77..1309a78 100644 --- a/adb/usb_windows.c +++ b/adb/usb_windows.c @@ -310,14 +310,14 @@ int usb_read(usb_handle *handle, void* data, int len) { int xfer = (len > 4096) ? 4096 : len; ret = AdbReadEndpointSync(handle->adb_read_pipe, - (void*)data, + data, (unsigned long)xfer, &read, time_out); int saved_errno = GetLastError(); D("usb_write got: %ld, expected: %d, errno: %d\n", read, xfer, saved_errno); if (ret) { - data += read; + data = (char *)data + read; len -= read; if (len == 0) diff --git a/adf/libadf/adf.c b/adf/libadf/adf.c index 871629e..1d19152 100644 --- a/adf/libadf/adf.c +++ b/adf/libadf/adf.c @@ -768,7 +768,7 @@ int adf_find_simple_post_configuration(struct adf_device *dev, const __u32 *formats, size_t n_formats, adf_id_t *interface, adf_id_t *overlay_engine) { - adf_id_t *intfs; + adf_id_t *intfs = NULL; ssize_t n_intfs = adf_interfaces(dev, &intfs); if (n_intfs < 0) diff --git a/adf/libadfhwc/adfhwc.cpp b/adf/libadfhwc/adfhwc.cpp index dee3cae..57e09eb 100644 --- a/adf/libadfhwc/adfhwc.cpp +++ b/adf/libadfhwc/adfhwc.cpp @@ -257,7 +257,7 @@ int adf_hwc_open(int *intf_fds, size_t n_intfs, ret = adf_set_event(dup_intf_fd, ADF_EVENT_HOTPLUG, 1); if (ret < 0 && ret != -EINVAL) { - ALOGE("failed to enable hotplug event on display %u: %s", + ALOGE("failed to enable hotplug event on display %zu: %s", i, strerror(errno)); goto err; } diff --git a/charger/Android.mk b/charger/Android.mk index b9d3473..40c7d78 100644 --- a/charger/Android.mk +++ b/charger/Android.mk @@ -24,7 +24,7 @@ LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED) LOCAL_C_INCLUDES := bootable/recovery -LOCAL_STATIC_LIBRARIES := libminui libpixelflinger_static libpng +LOCAL_STATIC_LIBRARIES := libminui libpng ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true) LOCAL_STATIC_LIBRARIES += libsuspend endif diff --git a/charger/charger.c b/charger/charger.c index 66ddeaf..15add87 100644 --- a/charger/charger.c +++ b/charger/charger.c @@ -40,6 +40,7 @@ #include <cutils/list.h> #include <cutils/misc.h> #include <cutils/uevent.h> +#include <cutils/properties.h> #ifdef CHARGER_ENABLE_SUSPEND #include <suspend/autosuspend.h> @@ -47,6 +48,8 @@ #include "minui/minui.h" +char *locale; + #ifndef max #define max(a,b) ((a) > (b) ? (a) : (b)) #endif @@ -89,7 +92,6 @@ struct power_supply { }; struct frame { - const char *name; int disp_time; int min_capacity; bool level_only; @@ -140,33 +142,27 @@ struct uevent { static struct frame batt_anim_frames[] = { { - .name = "charger/battery_0", .disp_time = 750, .min_capacity = 0, }, { - .name = "charger/battery_1", .disp_time = 750, .min_capacity = 20, }, { - .name = "charger/battery_2", .disp_time = 750, .min_capacity = 40, }, { - .name = "charger/battery_3", .disp_time = 750, .min_capacity = 60, }, { - .name = "charger/battery_4", .disp_time = 750, .min_capacity = 80, .level_only = true, }, { - .name = "charger/battery_5", .disp_time = 750, .min_capacity = BATTERY_FULL_THRESH, }, @@ -196,8 +192,8 @@ static int64_t curr_time_ms(void) static void clear_screen(void) { gr_color(0, 0, 0, 255); - gr_fill(0, 0, gr_fb_width(), gr_fb_height()); -}; + gr_clear(); +} #define MAX_KLOG_WRITE_BUF_SZ 256 @@ -657,8 +653,8 @@ static void draw_battery(struct charger *charger) if (batt_anim->num_frames != 0) { draw_surface_centered(charger, frame->surface); - LOGV("drawing frame #%d name=%s min_cap=%d time=%d\n", - batt_anim->cur_frame, frame->name, frame->min_capacity, + LOGV("drawing frame #%d min_cap=%d time=%d\n", + batt_anim->cur_frame, frame->min_capacity, frame->disp_time); } } @@ -835,8 +831,16 @@ static void process_key(struct charger *charger, int code, int64_t now) if (key->down) { int64_t reboot_timeout = key->timestamp + POWER_ON_KEY_TIME; if (now >= reboot_timeout) { - LOGI("[%lld] rebooting\n", now); - android_reboot(ANDROID_RB_RESTART, 0, 0); + /* We do not currently support booting from charger mode on + all devices. Check the property and continue booting or reboot + accordingly. */ + if (property_get_bool("ro.enable_boot_charger_mode", false)) { + LOGI("[%lld] booting from charger mode\n", now); + property_set("sys.boot_from_charger_mode", "1"); + } else { + LOGI("[%lld] rebooting\n", now); + android_reboot(ANDROID_RB_RESTART, 0, 0); + } } else { /* if the key is pressed but timeout hasn't expired, * make sure we wake up at the right-ish time to check @@ -978,22 +982,29 @@ int main(int argc, char **argv) charger->uevent_fd = fd; coldboot(charger, "/sys/class/power_supply", "add"); - ret = res_create_surface("charger/battery_fail", &charger->surf_unknown); + ret = res_create_display_surface("charger/battery_fail", &charger->surf_unknown); if (ret < 0) { - LOGE("Cannot load image\n"); + LOGE("Cannot load battery_fail image\n"); charger->surf_unknown = NULL; } - for (i = 0; i < charger->batt_anim->num_frames; i++) { - struct frame *frame = &charger->batt_anim->frames[i]; + charger->batt_anim = &battery_animation; - ret = res_create_surface(frame->name, &frame->surface); - if (ret < 0) { - LOGE("Cannot load image %s\n", frame->name); - /* TODO: free the already allocated surfaces... */ - charger->batt_anim->num_frames = 0; - charger->batt_anim->num_cycles = 1; - break; + gr_surface* scale_frames; + int scale_count; + ret = res_create_multi_display_surface("charger/battery_scale", &scale_count, &scale_frames); + if (ret < 0) { + LOGE("Cannot load battery_scale image\n"); + charger->batt_anim->num_frames = 0; + charger->batt_anim->num_cycles = 1; + } else if (scale_count != charger->batt_anim->num_frames) { + LOGE("battery_scale image has unexpected frame count (%d, expected %d)\n", + scale_count, charger->batt_anim->num_frames); + charger->batt_anim->num_frames = 0; + charger->batt_anim->num_cycles = 1; + } else { + for (i = 0; i < charger->batt_anim->num_frames; i++) { + charger->batt_anim->frames[i].surface = scale_frames[i]; } } diff --git a/charger/images/battery_0.png b/charger/images/battery_0.png Binary files differdeleted file mode 100644 index 2347074..0000000 --- a/charger/images/battery_0.png +++ /dev/null diff --git a/charger/images/battery_1.png b/charger/images/battery_1.png Binary files differdeleted file mode 100644 index cd34620..0000000 --- a/charger/images/battery_1.png +++ /dev/null diff --git a/charger/images/battery_2.png b/charger/images/battery_2.png Binary files differdeleted file mode 100644 index 3e4095e..0000000 --- a/charger/images/battery_2.png +++ /dev/null diff --git a/charger/images/battery_3.png b/charger/images/battery_3.png Binary files differdeleted file mode 100644 index 08c1551..0000000 --- a/charger/images/battery_3.png +++ /dev/null diff --git a/charger/images/battery_4.png b/charger/images/battery_4.png Binary files differdeleted file mode 100644 index 3a678da..0000000 --- a/charger/images/battery_4.png +++ /dev/null diff --git a/charger/images/battery_5.png b/charger/images/battery_5.png Binary files differdeleted file mode 100644 index d8dc40e..0000000 --- a/charger/images/battery_5.png +++ /dev/null diff --git a/charger/images/battery_charge.png b/charger/images/battery_charge.png Binary files differdeleted file mode 100644 index b501933..0000000 --- a/charger/images/battery_charge.png +++ /dev/null diff --git a/charger/images/battery_fail.png b/charger/images/battery_fail.png Binary files differindex 36fc254..aded88a 100644 --- a/charger/images/battery_fail.png +++ b/charger/images/battery_fail.png diff --git a/charger/images/battery_scale.png b/charger/images/battery_scale.png Binary files differnew file mode 100644 index 0000000..2ae8f0f --- /dev/null +++ b/charger/images/battery_scale.png diff --git a/cpio/Android.mk b/cpio/Android.mk index 5184463..575beb2 100644 --- a/cpio/Android.mk +++ b/cpio/Android.mk @@ -8,6 +8,8 @@ LOCAL_SRC_FILES := \ LOCAL_MODULE := mkbootfs +LOCAL_CFLAGS := -Werror + include $(BUILD_HOST_EXECUTABLE) $(call dist-for-goals,dist_files,$(LOCAL_BUILT_MODULE)) diff --git a/cpio/mkbootfs.c b/cpio/mkbootfs.c index 7d3740c..7175749 100644 --- a/cpio/mkbootfs.c +++ b/cpio/mkbootfs.c @@ -78,8 +78,9 @@ static void fix_stat(const char *path, struct stat *s) s->st_mode = empty_path_config->mode | (s->st_mode & ~07777); } else { // Use the compiled-in fs_config() function. - - fs_config(path, S_ISDIR(s->st_mode), &s->st_uid, &s->st_gid, &s->st_mode, &capabilities); + unsigned st_mode = s->st_mode; + fs_config(path, S_ISDIR(s->st_mode), &s->st_uid, &s->st_gid, &st_mode, &capabilities); + s->st_mode = (typeof(s->st_mode)) st_mode; } } diff --git a/debuggerd/Android.mk b/debuggerd/Android.mk index 422a86a..c33b263 100644 --- a/debuggerd/Android.mk +++ b/debuggerd/Android.mk @@ -1,70 +1,67 @@ -# Copyright 2005 The Android Open Source Project - -ifneq ($(filter arm mips x86,$(TARGET_ARCH)),) - LOCAL_PATH:= $(call my-dir) + include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ - backtrace.cpp \ - debuggerd.cpp \ - getevent.cpp \ - tombstone.cpp \ - utility.cpp \ - $(TARGET_ARCH)/machine.cpp \ + backtrace.cpp \ + debuggerd.cpp \ + getevent.cpp \ + tombstone.cpp \ + utility.cpp \ -LOCAL_CONLYFLAGS := -std=gnu99 -LOCAL_CPPFLAGS := -std=gnu++11 -LOCAL_CFLAGS := \ - -Wall \ - -Wno-array-bounds \ - -Werror \ - -LOCAL_MODULE := debuggerd +LOCAL_SRC_FILES_arm := arm/machine.cpp +LOCAL_SRC_FILES_arm64 := arm64/machine.cpp +LOCAL_SRC_FILES_mips := mips/machine.cpp +LOCAL_SRC_FILES_mips64 := mips/machine.cpp +LOCAL_SRC_FILES_x86 := x86/machine.cpp +LOCAL_SRC_FILES_x86_64 := x86_64/machine.cpp -ifeq ($(ARCH_ARM_HAVE_VFP),true) -LOCAL_CFLAGS += -DWITH_VFP -endif # ARCH_ARM_HAVE_VFP -ifeq ($(ARCH_ARM_HAVE_VFP_D32),true) -LOCAL_CFLAGS += -DWITH_VFP_D32 -endif # ARCH_ARM_HAVE_VFP_D32 +LOCAL_CPPFLAGS := \ + -std=gnu++11 \ + -W -Wall -Wextra \ + -Wunused \ + -Werror \ LOCAL_SHARED_LIBRARIES := \ - libbacktrace \ - libc \ - libcutils \ - liblog \ - libselinux \ + libbacktrace \ + libcutils \ + liblog \ + libselinux \ include external/stlport/libstlport.mk +LOCAL_MODULE := debuggerd +LOCAL_MODULE_STEM_32 := debuggerd +LOCAL_MODULE_STEM_64 := debuggerd64 +LOCAL_MULTILIB := both +LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk + include $(BUILD_EXECUTABLE) + + include $(CLEAR_VARS) LOCAL_SRC_FILES := crasher.c -LOCAL_SRC_FILES += $(TARGET_ARCH)/crashglue.S -LOCAL_MODULE := crasher +LOCAL_SRC_FILES_arm := arm/crashglue.S +LOCAL_SRC_FILES_arm64 := arm64/crashglue.S +LOCAL_SRC_FILES_mips := mips/crashglue.S +LOCAL_SRC_FILES_mips64 := mips/crashglue.S +LOCAL_SRC_FILES_x86 := x86/crashglue.S +LOCAL_SRC_FILES_x86_64 := x86_64/crashglue.S LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := optional -LOCAL_CFLAGS += -fstack-protector-all -Wno-unused-parameter -Wno-free-nonheap-object +LOCAL_CFLAGS += -fstack-protector-all -Werror -Wno-free-nonheap-object #LOCAL_FORCE_STATIC_EXECUTABLE := true LOCAL_SHARED_LIBRARIES := libcutils liblog libc -include $(BUILD_EXECUTABLE) -ifeq ($(ARCH_ARM_HAVE_VFP),true) -include $(CLEAR_VARS) - -LOCAL_CFLAGS += -DWITH_VFP +# The arm emulator has VFP but not VFPv3-D32. ifeq ($(ARCH_ARM_HAVE_VFP_D32),true) -LOCAL_CFLAGS += -DWITH_VFP_D32 -endif # ARCH_ARM_HAVE_VFP_D32 +LOCAL_ASFLAGS_arm += -DHAS_VFP_D32 +endif -LOCAL_SRC_FILES := vfp-crasher.c vfp.S -LOCAL_MODULE := vfp-crasher -LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) -LOCAL_MODULE_TAGS := optional -LOCAL_SHARED_LIBRARIES := libcutils liblog libc -include $(BUILD_EXECUTABLE) -endif # ARCH_ARM_HAVE_VFP == true +LOCAL_MODULE := crasher +LOCAL_MODULE_STEM_32 := crasher +LOCAL_MODULE_STEM_64 := crasher64 +LOCAL_MULTILIB := both -endif # arm or x86 in TARGET_ARCH +include $(BUILD_EXECUTABLE) diff --git a/debuggerd/arm/crashglue.S b/debuggerd/arm/crashglue.S index eb9d0e3..4fbfd6e 100644 --- a/debuggerd/arm/crashglue.S +++ b/debuggerd/arm/crashglue.S @@ -1,8 +1,5 @@ .globl crash1 .type crash1, %function -.globl crashnostack -.type crashnostack, %function - crash1: ldr r0, =0xa5a50000 ldr r1, =0xa5a50001 @@ -18,11 +15,48 @@ crash1: ldr r11, =0xa5a50011 ldr r12, =0xa5a50012 + + fconstd d0, #0 + fconstd d1, #1 + fconstd d2, #2 + fconstd d3, #3 + fconstd d4, #4 + fconstd d5, #5 + fconstd d6, #6 + fconstd d7, #7 + fconstd d8, #8 + fconstd d9, #9 + fconstd d10, #10 + fconstd d11, #11 + fconstd d12, #12 + fconstd d13, #13 + fconstd d14, #14 + fconstd d15, #15 +#if defined(HAS_VFP_D32) + fconstd d16, #16 + fconstd d17, #17 + fconstd d18, #18 + fconstd d19, #19 + fconstd d20, #20 + fconstd d21, #21 + fconstd d22, #22 + fconstd d23, #23 + fconstd d24, #24 + fconstd d25, #25 + fconstd d26, #26 + fconstd d27, #27 + fconstd d28, #28 + fconstd d29, #29 + fconstd d30, #30 + fconstd d31, #31 +#endif + mov lr, #0 ldr lr, [lr] b . - +.globl crashnostack +.type crashnostack, %function crashnostack: mov sp, #0 mov r0, #0 diff --git a/debuggerd/arm/machine.cpp b/debuggerd/arm/machine.cpp index 3fba6db..8270066 100644 --- a/debuggerd/arm/machine.cpp +++ b/debuggerd/arm/machine.cpp @@ -27,146 +27,68 @@ #include "../utility.h" #include "../machine.h" -// enable to dump memory pointed to by every register -#define DUMP_MEMORY_FOR_ALL_REGISTERS 1 - -#ifdef WITH_VFP -#ifdef WITH_VFP_D32 -#define NUM_VFP_REGS 32 -#else -#define NUM_VFP_REGS 16 -#endif -#endif - -static void dump_memory(log_t* log, pid_t tid, uintptr_t addr, int scope_flags) { - char code_buffer[64]; // actual 8+1+((8+1)*4) + 1 == 45 - char ascii_buffer[32]; // actual 16 + 1 == 17 - uintptr_t p, end; - - p = addr & ~3; - p -= 32; - if (p > addr) { - // catch underflow - p = 0; - } - // Dump more memory content for the crashing thread. - end = p + 256; - // catch overflow; 'end - p' has to be multiples of 16 - while (end < p) - end -= 16; - - // Dump the code around PC as: - // addr contents ascii - // 00008d34 ef000000 e8bd0090 e1b00000 512fff1e ............../Q - // 00008d44 ea00b1f9 e92d0090 e3a070fc ef000000 ......-..p...... - while (p < end) { - char* asc_out = ascii_buffer; - - sprintf(code_buffer, "%08x ", p); - - int i; - for (i = 0; i < 4; i++) { - // If we see (data == -1 && errno != 0), we know that the ptrace - // call failed, probably because we're dumping memory in an - // unmapped or inaccessible page. I don't know if there's - // value in making that explicit in the output -- it likely - // just complicates parsing and clarifies nothing for the - // enlightened reader. - long data = ptrace(PTRACE_PEEKTEXT, tid, reinterpret_cast<void*>(p), NULL); - sprintf(code_buffer + strlen(code_buffer), "%08lx ", data); - - // Enable the following code blob to dump ASCII values -#if 0 - int j; - for (j = 0; j < 4; j++) { - // Our isprint() allows high-ASCII characters that display - // differently (often badly) in different viewers, so we - // just use a simpler test. - char val = (data >> (j*8)) & 0xff; - if (val >= 0x20 && val < 0x7f) { - *asc_out++ = val; - } else { - *asc_out++ = '.'; - } - } -#endif - p += 4; - } - *asc_out = '\0'; - _LOG(log, scope_flags, " %s %s\n", code_buffer, ascii_buffer); - } -} - -// If configured to do so, dump memory around *all* registers -// for the crashing thread. -void dump_memory_and_code(log_t* log, pid_t tid, int scope_flags) { - struct pt_regs regs; +void dump_memory_and_code(log_t* log, pid_t tid) { + pt_regs regs; if (ptrace(PTRACE_GETREGS, tid, 0, ®s)) { return; } - if (IS_AT_FAULT(scope_flags) && DUMP_MEMORY_FOR_ALL_REGISTERS) { - static const char REG_NAMES[] = "r0r1r2r3r4r5r6r7r8r9slfpipsp"; + static const char REG_NAMES[] = "r0r1r2r3r4r5r6r7r8r9slfpipsp"; - for (int reg = 0; reg < 14; reg++) { - // this may not be a valid way to access, but it'll do for now - uintptr_t addr = regs.uregs[reg]; + for (int reg = 0; reg < 14; reg++) { + // this may not be a valid way to access, but it'll do for now + uintptr_t addr = regs.uregs[reg]; - // Don't bother if it looks like a small int or ~= null, or if - // it's in the kernel area. - if (addr < 4096 || addr >= 0xc0000000) { - continue; - } - - _LOG(log, scope_flags | SCOPE_SENSITIVE, "\nmemory near %.2s:\n", ®_NAMES[reg * 2]); - dump_memory(log, tid, addr, scope_flags | SCOPE_SENSITIVE); + // Don't bother if it looks like a small int or ~= null, or if + // it's in the kernel area. + if (addr < 4096 || addr >= 0xc0000000) { + continue; } + + _LOG(log, logtype::MEMORY, "\nmemory near %.2s:\n", ®_NAMES[reg * 2]); + dump_memory(log, tid, addr); } // explicitly allow upload of code dump logging - _LOG(log, scope_flags, "\ncode around pc:\n"); - dump_memory(log, tid, static_cast<uintptr_t>(regs.ARM_pc), scope_flags); + _LOG(log, logtype::MEMORY, "\ncode around pc:\n"); + dump_memory(log, tid, static_cast<uintptr_t>(regs.ARM_pc)); if (regs.ARM_pc != regs.ARM_lr) { - _LOG(log, scope_flags, "\ncode around lr:\n"); - dump_memory(log, tid, static_cast<uintptr_t>(regs.ARM_lr), scope_flags); + _LOG(log, logtype::MEMORY, "\ncode around lr:\n"); + dump_memory(log, tid, static_cast<uintptr_t>(regs.ARM_lr)); } } -void dump_registers(log_t* log, pid_t tid, int scope_flags) { - struct pt_regs r; +void dump_registers(log_t* log, pid_t tid) { + pt_regs r; if (ptrace(PTRACE_GETREGS, tid, 0, &r)) { - _LOG(log, scope_flags, "cannot get registers: %s\n", strerror(errno)); + _LOG(log, logtype::REGISTERS, "cannot get registers: %s\n", strerror(errno)); return; } - _LOG(log, scope_flags, " r0 %08x r1 %08x r2 %08x r3 %08x\n", + _LOG(log, logtype::REGISTERS, " r0 %08x r1 %08x r2 %08x r3 %08x\n", static_cast<uint32_t>(r.ARM_r0), static_cast<uint32_t>(r.ARM_r1), static_cast<uint32_t>(r.ARM_r2), static_cast<uint32_t>(r.ARM_r3)); - _LOG(log, scope_flags, " r4 %08x r5 %08x r6 %08x r7 %08x\n", + _LOG(log, logtype::REGISTERS, " r4 %08x r5 %08x r6 %08x r7 %08x\n", static_cast<uint32_t>(r.ARM_r4), static_cast<uint32_t>(r.ARM_r5), static_cast<uint32_t>(r.ARM_r6), static_cast<uint32_t>(r.ARM_r7)); - _LOG(log, scope_flags, " r8 %08x r9 %08x sl %08x fp %08x\n", + _LOG(log, logtype::REGISTERS, " r8 %08x r9 %08x sl %08x fp %08x\n", static_cast<uint32_t>(r.ARM_r8), static_cast<uint32_t>(r.ARM_r9), static_cast<uint32_t>(r.ARM_r10), static_cast<uint32_t>(r.ARM_fp)); - _LOG(log, scope_flags, " ip %08x sp %08x lr %08x pc %08x cpsr %08x\n", + _LOG(log, logtype::REGISTERS, " ip %08x sp %08x lr %08x pc %08x cpsr %08x\n", static_cast<uint32_t>(r.ARM_ip), static_cast<uint32_t>(r.ARM_sp), static_cast<uint32_t>(r.ARM_lr), static_cast<uint32_t>(r.ARM_pc), static_cast<uint32_t>(r.ARM_cpsr)); -#ifdef WITH_VFP - struct user_vfp vfp_regs; - int i; - + user_vfp vfp_regs; if (ptrace(PTRACE_GETVFPREGS, tid, 0, &vfp_regs)) { - _LOG(log, scope_flags, "cannot get registers: %s\n", strerror(errno)); + _LOG(log, logtype::REGISTERS, "cannot get registers: %s\n", strerror(errno)); return; } - for (i = 0; i < NUM_VFP_REGS; i += 2) { - _LOG(log, scope_flags, " d%-2d %016llx d%-2d %016llx\n", + for (size_t i = 0; i < 32; i += 2) { + _LOG(log, logtype::REGISTERS, " d%-2d %016llx d%-2d %016llx\n", i, vfp_regs.fpregs[i], i+1, vfp_regs.fpregs[i+1]); } - _LOG(log, scope_flags, " scr %08lx\n", vfp_regs.fpscr); -#endif + _LOG(log, logtype::REGISTERS, " scr %08lx\n", vfp_regs.fpscr); } diff --git a/debuggerd/arm64/crashglue.S b/debuggerd/arm64/crashglue.S new file mode 100644 index 0000000..e58b542 --- /dev/null +++ b/debuggerd/arm64/crashglue.S @@ -0,0 +1,79 @@ +.globl crash1 +.type crash1, %function +crash1: + ldr x0, =0xa5a50000 + ldr x1, =0xa5a50001 + ldr x2, =0xa5a50002 + ldr x3, =0xa5a50003 + ldr x4, =0xa5a50004 + ldr x5, =0xa5a50005 + ldr x6, =0xa5a50006 + ldr x7, =0xa5a50007 + ldr x8, =0xa5a50008 + ldr x9, =0xa5a50009 + ldr x10, =0xa5a50010 + ldr x11, =0xa5a50011 + ldr x12, =0xa5a50012 + ldr x13, =0xa5a50013 + ldr x14, =0xa5a50014 + ldr x15, =0xa5a50015 + ldr x16, =0xa5a50016 + ldr x17, =0xa5a50017 + ldr x18, =0xa5a50018 + ldr x19, =0xa5a50019 + ldr x20, =0xa5a50020 + ldr x21, =0xa5a50021 + ldr x22, =0xa5a50022 + ldr x23, =0xa5a50023 + ldr x24, =0xa5a50024 + ldr x25, =0xa5a50025 + ldr x26, =0xa5a50026 + ldr x27, =0xa5a50027 + ldr x28, =0xa5a50028 + ldr x29, =0xa5a50029 + + fmov d0, -1.0 // -1 is more convincing than 0. + fmov d1, 1.0 + fmov d2, 2.0 + fmov d3, 3.0 + fmov d4, 4.0 + fmov d5, 5.0 + fmov d6, 6.0 + fmov d7, 7.0 + fmov d8, 8.0 + fmov d9, 9.0 + fmov d10, 10.0 + fmov d11, 11.0 + fmov d12, 12.0 + fmov d13, 13.0 + fmov d14, 14.0 + fmov d15, 15.0 + fmov d16, 16.0 + fmov d17, 17.0 + fmov d18, 18.0 + fmov d19, 19.0 + fmov d20, 20.0 + fmov d21, 21.0 + fmov d22, 22.0 + fmov d23, 23.0 + fmov d24, 24.0 + fmov d25, 25.0 + fmov d26, 26.0 + fmov d27, 27.0 + fmov d28, 28.0 + fmov d29, 29.0 + fmov d30, 30.0 + fmov d31, 31.0 + + mov x30, xzr + ldr x30, [x30] + b . + + +.globl crashnostack +.type crashnostack, %function +crashnostack: + mov x0, xzr + add sp, x0, xzr + ldr x0, [x0] + b . diff --git a/debuggerd/arm64/machine.cpp b/debuggerd/arm64/machine.cpp new file mode 100644 index 0000000..ec664bd --- /dev/null +++ b/debuggerd/arm64/machine.cpp @@ -0,0 +1,112 @@ +/* + * + * Copyright 2014, 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 <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <stdio.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/ptrace.h> +#include <sys/user.h> +#include <sys/uio.h> +#include <linux/elf.h> + +#include "../utility.h" +#include "../machine.h" + +void dump_memory_and_code(log_t* log, pid_t tid) { + struct user_pt_regs regs; + struct iovec io; + io.iov_base = ®s; + io.iov_len = sizeof(regs); + + if (ptrace(PTRACE_GETREGSET, tid, (void*)NT_PRSTATUS, &io) == -1) { + _LOG(log, logtype::ERROR, "%s: ptrace failed to get registers: %s\n", + __func__, strerror(errno)); + return; + } + + for (int reg = 0; reg < 31; reg++) { + uintptr_t addr = regs.regs[reg]; + + /* + * Don't bother if it looks like a small int or ~= null, or if + * it's in the kernel area. + */ + if (addr < 4096 || addr >= (1UL<<63)) { + continue; + } + + _LOG(log, logtype::MEMORY, "\nmemory near x%d:\n", reg); + dump_memory(log, tid, addr); + } + + _LOG(log, logtype::MEMORY, "\ncode around pc:\n"); + dump_memory(log, tid, (uintptr_t)regs.pc); + + if (regs.pc != regs.sp) { + _LOG(log, logtype::MEMORY, "\ncode around sp:\n"); + dump_memory(log, tid, (uintptr_t)regs.sp); + } +} + +void dump_registers(log_t* log, pid_t tid) { + struct user_pt_regs r; + struct iovec io; + io.iov_base = &r; + io.iov_len = sizeof(r); + + if (ptrace(PTRACE_GETREGSET, tid, (void*) NT_PRSTATUS, (void*) &io) == -1) { + _LOG(log, logtype::ERROR, "ptrace error: %s\n", strerror(errno)); + return; + } + + for (int i = 0; i < 28; i += 4) { + _LOG(log, logtype::REGISTERS, + " x%-2d %016lx x%-2d %016lx x%-2d %016lx x%-2d %016lx\n", + i, (uint64_t)r.regs[i], + i+1, (uint64_t)r.regs[i+1], + i+2, (uint64_t)r.regs[i+2], + i+3, (uint64_t)r.regs[i+3]); + } + + _LOG(log, logtype::REGISTERS, " x28 %016lx x29 %016lx x30 %016lx\n", + (uint64_t)r.regs[28], (uint64_t)r.regs[29], (uint64_t)r.regs[30]); + + _LOG(log, logtype::REGISTERS, " sp %016lx pc %016lx\n", + (uint64_t)r.sp, (uint64_t)r.pc); + + + struct user_fpsimd_state f; + io.iov_base = &f; + io.iov_len = sizeof(f); + + if (ptrace(PTRACE_GETREGSET, tid, (void*) NT_PRFPREG, (void*) &io) == -1) { + _LOG(log, logtype::ERROR, "ptrace error: %s\n", strerror(errno)); + return; + } + + for (int i = 0; i < 32; i += 4) { + _LOG(log, logtype::REGISTERS, " v%-2d %016lx v%-2d %016lx v%-2d %016lx v%-2d %016lx\n", + i, (uint64_t)f.vregs[i], + i+1, (uint64_t)f.vregs[i+1], + i+2, (uint64_t)f.vregs[i+2], + i+3, (uint64_t)f.vregs[i+3]); + } +} diff --git a/debuggerd/backtrace.cpp b/debuggerd/backtrace.cpp index d388348..c4a2143 100644 --- a/debuggerd/backtrace.cpp +++ b/debuggerd/backtrace.cpp @@ -30,6 +30,7 @@ #include <UniquePtr.h> #include "backtrace.h" + #include "utility.h" static void dump_process_header(log_t* log, pid_t pid) { @@ -48,16 +49,17 @@ static void dump_process_header(log_t* log, pid_t pid) { struct tm tm; localtime_r(&t, &tm); char timestr[64]; + _LOG(log, logtype::BACKTRACE, "\n\nABI: '%s'\n", ABI_STRING); strftime(timestr, sizeof(timestr), "%F %T", &tm); - _LOG(log, SCOPE_AT_FAULT, "\n\n----- pid %d at %s -----\n", pid, timestr); + _LOG(log, logtype::BACKTRACE, "\n----- pid %d at %s -----\n", pid, timestr); if (procname) { - _LOG(log, SCOPE_AT_FAULT, "Cmd line: %s\n", procname); + _LOG(log, logtype::BACKTRACE, "Cmd line: %s\n", procname); } } static void dump_process_footer(log_t* log, pid_t pid) { - _LOG(log, SCOPE_AT_FAULT, "\n----- end %d -----\n", pid); + _LOG(log, logtype::BACKTRACE, "\n----- end %d -----\n", pid); } static void dump_thread( @@ -79,10 +81,10 @@ static void dump_thread( } } - _LOG(log, SCOPE_AT_FAULT, "\n\"%s\" sysTid=%d\n", threadname ? threadname : "<unknown>", tid); + _LOG(log, logtype::BACKTRACE, "\n\"%s\" sysTid=%d\n", threadname ? threadname : "<unknown>", tid); if (!attached && ptrace(PTRACE_ATTACH, tid, 0, 0) < 0) { - _LOG(log, SCOPE_AT_FAULT, "Could not attach to thread: %s\n", strerror(errno)); + _LOG(log, logtype::BACKTRACE, "Could not attach to thread: %s\n", strerror(errno)); return; } @@ -90,11 +92,11 @@ static void dump_thread( UniquePtr<Backtrace> backtrace(Backtrace::Create(tid, BACKTRACE_CURRENT_THREAD)); if (backtrace->Unwind(0)) { - dump_backtrace_to_log(backtrace.get(), log, SCOPE_AT_FAULT, " "); + dump_backtrace_to_log(backtrace.get(), log, " "); } if (!attached && ptrace(PTRACE_DETACH, tid, 0, 0) != 0) { - LOG("ptrace detach from %d failed: %s\n", tid, strerror(errno)); + _LOG(log, logtype::ERROR, "ptrace detach from %d failed: %s\n", tid, strerror(errno)); *detach_failed = true; } } @@ -104,7 +106,6 @@ void dump_backtrace(int fd, int amfd, pid_t pid, pid_t tid, bool* detach_failed, log_t log; log.tfd = fd; log.amfd = amfd; - log.quiet = true; dump_process_header(&log, pid); dump_thread(&log, tid, true, detach_failed, total_sleep_time_usec); @@ -133,9 +134,8 @@ void dump_backtrace(int fd, int amfd, pid_t pid, pid_t tid, bool* detach_failed, dump_process_footer(&log, pid); } -void dump_backtrace_to_log(Backtrace* backtrace, log_t* log, - int scope_flags, const char* prefix) { +void dump_backtrace_to_log(Backtrace* backtrace, log_t* log, const char* prefix) { for (size_t i = 0; i < backtrace->NumFrames(); i++) { - _LOG(log, scope_flags, "%s%s\n", prefix, backtrace->FormatFrameData(i).c_str()); + _LOG(log, logtype::BACKTRACE, "%s%s\n", prefix, backtrace->FormatFrameData(i).c_str()); } } diff --git a/debuggerd/backtrace.h b/debuggerd/backtrace.h index 2ec8afb..da14cd4 100644 --- a/debuggerd/backtrace.h +++ b/debuggerd/backtrace.h @@ -29,7 +29,6 @@ void dump_backtrace(int fd, int amfd, pid_t pid, pid_t tid, bool* detach_failed, int* total_sleep_time_usec); /* Dumps the backtrace in the backtrace data structure to the log. */ -void dump_backtrace_to_log(Backtrace* backtrace, log_t* log, - int scope_flags, const char* prefix); +void dump_backtrace_to_log(Backtrace* backtrace, log_t* log, const char* prefix); #endif // _DEBUGGERD_BACKTRACE_H diff --git a/debuggerd/crasher.c b/debuggerd/crasher.c index 5ecb1a5..9df3c64 100644 --- a/debuggerd/crasher.c +++ b/debuggerd/crasher.c @@ -1,21 +1,24 @@ - -//#include <cutils/misc.h> - -#include <unistd.h> +#include <assert.h> +#include <errno.h> +#include <pthread.h> +#include <sched.h> +#include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <sched.h> -#include <errno.h> - -#include <signal.h> +#include <sys/cdefs.h> +#include <sys/mman.h> #include <sys/ptrace.h> -#include <sys/wait.h> #include <sys/socket.h> - -#include <pthread.h> +#include <sys/wait.h> +#include <unistd.h> #include <cutils/sockets.h> +#include <log/log.h> + +#ifndef __unused +#define __unused __attribute__((__unused__)) +#endif extern const char* __progname; @@ -23,26 +26,13 @@ void crash1(void); void crashnostack(void); static int do_action(const char* arg); -static void debuggerd_connect() -{ - char tmp[1]; - int s; - sprintf(tmp, "%d", gettid()); - s = socket_local_client("android:debuggerd", - ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM); - if(s >= 0) { - read(s, tmp, 1); - close(s); - } -} - -static void maybeabort() { - if(time(0) != 42) { +static void maybe_abort() { + if (time(0) != 42) { abort(); } } -static int smash_stack(int i) { +static int smash_stack(int i __unused) { printf("crasher: deliberately corrupting stack...\n"); // Unless there's a "big enough" buffer on the stack, gcc // doesn't bother inserting checks. @@ -63,20 +53,15 @@ __attribute__((noinline)) static void overflow_stack(void* p) { overflow_stack(&buf); } -static void test_call1() -{ - *((int*) 32) = 1; -} - static void *noisy(void *x) { - char c = (unsigned) x; + char c = (uintptr_t) x; for(;;) { usleep(250*1000); write(2, &c, 1); if(c == 'C') *((unsigned*) 0) = 42; } - return 0; + return NULL; } static int ctest() @@ -94,7 +79,7 @@ static int ctest() static void* thread_callback(void* raw_arg) { - return (void*) do_action((const char*) raw_arg); + return (void*) (uintptr_t) do_action((const char*) raw_arg); } static int do_action_on_thread(const char* arg) @@ -103,7 +88,7 @@ static int do_action_on_thread(const char* arg) pthread_create(&t, NULL, thread_callback, (void*) arg); void* result = NULL; pthread_join(t, &result); - return (int) result; + return (int) (uintptr_t) result; } __attribute__((noinline)) static int crash3(int a) { @@ -126,41 +111,77 @@ static void abuse_heap() { free((void*) buf); // GCC is smart enough to warn about this, but we're doing it deliberately. } +static void sigsegv_non_null() { + int* a = (int *)(&do_action); + *a = 42; +} + static int do_action(const char* arg) { fprintf(stderr,"crasher: init pid=%d tid=%d\n", getpid(), gettid()); if (!strncmp(arg, "thread-", strlen("thread-"))) { return do_action_on_thread(arg + strlen("thread-")); - } else if (!strcmp(arg,"smash-stack")) { + } else if (!strcmp(arg, "SIGSEGV-non-null")) { + sigsegv_non_null(); + } else if (!strcmp(arg, "smash-stack")) { return smash_stack(42); - } else if (!strcmp(arg,"stack-overflow")) { + } else if (!strcmp(arg, "stack-overflow")) { overflow_stack(NULL); - } else if (!strcmp(arg,"nostack")) { + } else if (!strcmp(arg, "nostack")) { crashnostack(); - } else if (!strcmp(arg,"ctest")) { + } else if (!strcmp(arg, "ctest")) { return ctest(); - } else if (!strcmp(arg,"exit")) { + } else if (!strcmp(arg, "exit")) { exit(1); - } else if (!strcmp(arg,"crash")) { + } else if (!strcmp(arg, "crash") || !strcmp(arg, "SIGSEGV")) { return crash(42); - } else if (!strcmp(arg,"abort")) { - maybeabort(); + } else if (!strcmp(arg, "abort")) { + maybe_abort(); + } else if (!strcmp(arg, "assert")) { + __assert("some_file.c", 123, "false"); + } else if (!strcmp(arg, "assert2")) { + __assert2("some_file.c", 123, "some_function", "false"); + } else if (!strcmp(arg, "LOG_ALWAYS_FATAL")) { + LOG_ALWAYS_FATAL("hello %s", "world"); + } else if (!strcmp(arg, "LOG_ALWAYS_FATAL_IF")) { + LOG_ALWAYS_FATAL_IF(true, "hello %s", "world"); + } else if (!strcmp(arg, "SIGPIPE")) { + int pipe_fds[2]; + pipe(pipe_fds); + close(pipe_fds[0]); + write(pipe_fds[1], "oops", 4); + return EXIT_SUCCESS; + } else if (!strcmp(arg, "SIGTRAP")) { + raise(SIGTRAP); + return EXIT_SUCCESS; } else if (!strcmp(arg, "heap-usage")) { abuse_heap(); + } else if (!strcmp(arg, "SIGSEGV-unmapped")) { + char* map = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); + munmap(map, sizeof(int)); + map[0] = '8'; } fprintf(stderr, "%s OP\n", __progname); fprintf(stderr, "where OP is:\n"); - fprintf(stderr, " smash-stack overwrite a stack-guard canary\n"); - fprintf(stderr, " stack-overflow recurse until the stack overflows\n"); - fprintf(stderr, " heap-corruption cause a libc abort by corrupting the heap\n"); - fprintf(stderr, " heap-usage cause a libc abort by abusing a heap function\n"); - fprintf(stderr, " nostack crash with a NULL stack pointer\n"); - fprintf(stderr, " ctest (obsoleted by thread-crash?)\n"); - fprintf(stderr, " exit call exit(1)\n"); - fprintf(stderr, " crash cause a SIGSEGV\n"); - fprintf(stderr, " abort call abort()\n"); + fprintf(stderr, " smash-stack overwrite a stack-guard canary\n"); + fprintf(stderr, " stack-overflow recurse until the stack overflows\n"); + fprintf(stderr, " heap-corruption cause a libc abort by corrupting the heap\n"); + fprintf(stderr, " heap-usage cause a libc abort by abusing a heap function\n"); + fprintf(stderr, " nostack crash with a NULL stack pointer\n"); + fprintf(stderr, " ctest (obsoleted by thread-crash?)\n"); + fprintf(stderr, " exit call exit(1)\n"); + fprintf(stderr, " abort call abort()\n"); + fprintf(stderr, " assert call assert() without a function\n"); + fprintf(stderr, " assert2 call assert() with a function\n"); + fprintf(stderr, " LOG_ALWAYS_FATAL call LOG_ALWAYS_FATAL\n"); + fprintf(stderr, " LOG_ALWAYS_FATAL_IF call LOG_ALWAYS_FATAL\n"); + fprintf(stderr, " SIGPIPE cause a SIGPIPE\n"); + fprintf(stderr, " SIGSEGV cause a SIGSEGV at address 0x0 (synonym: crash)\n"); + fprintf(stderr, " SIGSEGV-non-null cause a SIGSEGV at a non-zero address\n"); + fprintf(stderr, " SIGSEGV-unmapped mmap/munmap a region of memory and then attempt to access it\n"); + fprintf(stderr, " SIGTRAP cause a SIGTRAP\n"); fprintf(stderr, "prefix any of the above with 'thread-' to not run\n"); fprintf(stderr, "on the process' main thread.\n"); return EXIT_SUCCESS; diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp index de8ba9d..61805c9 100644 --- a/debuggerd/debuggerd.cpp +++ b/debuggerd/debuggerd.cpp @@ -30,7 +30,6 @@ #include <sys/stat.h> #include <sys/poll.h> -#include <log/logd.h> #include <log/logger.h> #include <cutils/sockets.h> @@ -46,105 +45,55 @@ #include "tombstone.h" #include "utility.h" -typedef struct { +struct debugger_request_t { debugger_action_t action; pid_t pid, tid; uid_t uid, gid; uintptr_t abort_msg_address; -} debugger_request_t; - -static int write_string(const char* file, const char* string) { - int len; - int fd; - ssize_t amt; - fd = open(file, O_RDWR); - len = strlen(string); - if (fd < 0) - return -errno; - amt = write(fd, string, len); - close(fd); - return amt >= 0 ? 0 : -errno; -} - -static void init_debug_led() { - // trout leds - write_string("/sys/class/leds/red/brightness", "0"); - write_string("/sys/class/leds/green/brightness", "0"); - write_string("/sys/class/leds/blue/brightness", "0"); - write_string("/sys/class/leds/red/device/blink", "0"); - // sardine leds - write_string("/sys/class/leds/left/cadence", "0,0"); -} + int32_t original_si_code; +}; -static void enable_debug_led() { - // trout leds - write_string("/sys/class/leds/red/brightness", "255"); - // sardine leds - write_string("/sys/class/leds/left/cadence", "1,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); -static void disable_debug_led() { - // trout leds - write_string("/sys/class/leds/red/brightness", "0"); - // sardine leds - write_string("/sys/class/leds/left/cadence", "0,0"); -} + 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'; + } -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 + // 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) { - 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; - struct 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 (true) { + input_event e; + if (get_event(&e, -1) == 0) { + if (e.type == EV_KEY && e.code == KEY_VOLUMEDOWN && e.value == 0) { + break; } } - } while (!done); + } uninit_getevent(); } - // don't forget to turn debug led off - disable_debug_led(); - LOG("debuggerd resuming process %d", pid); + ALOGI("debuggerd resuming process %d", request.pid); } static int get_process_info(pid_t tid, pid_t* out_pid, uid_t* out_uid, uid_t* out_gid) { @@ -176,24 +125,24 @@ static int get_process_info(pid_t tid, pid_t* out_pid, uid_t* out_uid, uid_t* ou } static int read_request(int fd, debugger_request_t* out_request) { - struct ucred cr; - int len = sizeof(cr); + ucred cr; + socklen_t len = sizeof(cr); int status = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &len); if (status != 0) { - LOG("cannot get credentials\n"); + ALOGE("cannot get credentials\n"); return -1; } - XLOG("reading tid\n"); + ALOGV("reading tid\n"); fcntl(fd, F_SETFL, O_NONBLOCK); - struct pollfd pollfds[1]; + pollfd pollfds[1]; pollfds[0].fd = fd; pollfds[0].events = POLLIN; pollfds[0].revents = 0; status = TEMP_FAILURE_RETRY(poll(pollfds, 1, 3000)); if (status != 1) { - LOG("timed out reading tid (from pid=%d uid=%d)\n", cr.pid, cr.uid); + ALOGE("timed out reading tid (from pid=%d uid=%d)\n", cr.pid, cr.uid); return -1; } @@ -201,13 +150,11 @@ static int read_request(int fd, debugger_request_t* out_request) { memset(&msg, 0, sizeof(msg)); status = TEMP_FAILURE_RETRY(read(fd, &msg, sizeof(msg))); if (status < 0) { - LOG("read failure? %s (pid=%d uid=%d)\n", strerror(errno), cr.pid, cr.uid); + ALOGE("read failure? %s (pid=%d uid=%d)\n", strerror(errno), cr.pid, cr.uid); return -1; } - if (status == sizeof(debugger_msg_t)) { - XLOG("crash request of size %d abort_msg_address=%#08x\n", status, msg.abort_msg_address); - } else { - LOG("invalid crash request of size %d (from pid=%d uid=%d)\n", status, cr.pid, cr.uid); + if (status != sizeof(debugger_msg_t)) { + ALOGE("invalid crash request of size %d (from pid=%d uid=%d)\n", status, cr.pid, cr.uid); return -1; } @@ -217,6 +164,7 @@ static int read_request(int fd, debugger_request_t* out_request) { out_request->uid = cr.uid; out_request->gid = cr.gid; out_request->abort_msg_address = msg.abort_msg_address; + out_request->original_si_code = msg.original_si_code; if (msg.action == DEBUGGER_ACTION_CRASH) { // Ensure that the tid reported by the crashing process is valid. @@ -224,7 +172,7 @@ static int read_request(int fd, debugger_request_t* out_request) { struct stat s; snprintf(buf, sizeof buf, "/proc/%d/task/%d", out_request->pid, out_request->tid); if (stat(buf, &s)) { - LOG("tid %d does not exist in pid %d. ignoring debug request\n", + ALOGE("tid %d does not exist in pid %d. ignoring debug request\n", out_request->tid, out_request->pid); return -1; } @@ -235,7 +183,7 @@ static int read_request(int fd, debugger_request_t* out_request) { status = get_process_info(out_request->tid, &out_request->pid, &out_request->uid, &out_request->gid); if (status < 0) { - LOG("tid %d does not exist. ignoring explicit dump request\n", out_request->tid); + ALOGE("tid %d does not exist. ignoring explicit dump request\n", out_request->tid); return -1; } } else { @@ -256,13 +204,13 @@ static bool should_attach_gdb(debugger_request_t* request) { } static void handle_request(int fd) { - XLOG("handle_request(%d)\n", fd); + ALOGV("handle_request(%d)\n", fd); debugger_request_t request; memset(&request, 0, sizeof(request)); int status = read_request(fd, &request); if (!status) { - XLOG("BOOM: pid=%d uid=%d gid=%d tid=%d\n", + ALOGV("BOOM: pid=%d uid=%d gid=%d tid=%d\n", request.pid, request.uid, request.gid, request.tid); // At this point, the thread that made the request is blocked in @@ -276,12 +224,12 @@ static void handle_request(int fd) { // See details in bionic/libc/linker/debugger.c, in function // debugger_signal_handler(). if (ptrace(PTRACE_ATTACH, request.tid, 0, 0)) { - LOG("ptrace attach failed: %s\n", strerror(errno)); + ALOGE("ptrace attach failed: %s\n", strerror(errno)); } else { bool detach_failed = false; bool attach_gdb = should_attach_gdb(&request); if (TEMP_FAILURE_RETRY(write(fd, "\0", 1)) != 1) { - LOG("failed responding to client: %s\n", strerror(errno)); + ALOGE("failed responding to client: %s\n", strerror(errno)); } else { char* tombstone_path = NULL; @@ -300,34 +248,36 @@ static void handle_request(int fd) { switch (signal) { case SIGSTOP: if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) { - XLOG("stopped -- dumping to tombstone\n"); - tombstone_path = engrave_tombstone( - request.pid, request.tid, signal, request.abort_msg_address, true, true, - &detach_failed, &total_sleep_time_usec); + ALOGV("stopped -- dumping to tombstone\n"); + tombstone_path = engrave_tombstone(request.pid, request.tid, + signal, request.original_si_code, + request.abort_msg_address, true, + &detach_failed, &total_sleep_time_usec); } else if (request.action == DEBUGGER_ACTION_DUMP_BACKTRACE) { - XLOG("stopped -- dumping to fd\n"); + ALOGV("stopped -- dumping to fd\n"); dump_backtrace(fd, -1, request.pid, request.tid, &detach_failed, &total_sleep_time_usec); } else { - XLOG("stopped -- continuing\n"); + ALOGV("stopped -- continuing\n"); status = ptrace(PTRACE_CONT, request.tid, 0, 0); if (status) { - LOG("ptrace continue failed: %s\n", strerror(errno)); + ALOGE("ptrace continue failed: %s\n", strerror(errno)); } continue; // loop again } break; - case SIGILL: case SIGABRT: case SIGBUS: case SIGFPE: - case SIGSEGV: + case SIGILL: case SIGPIPE: + case SIGSEGV: #ifdef SIGSTKFLT case SIGSTKFLT: #endif - XLOG("stopped -- fatal signal\n"); + case SIGTRAP: + ALOGV("stopped -- fatal signal\n"); // Send a SIGSTOP to the process to make all of // the non-signaled threads stop moving. Without // this we get a lot of "ptrace detach failed: @@ -335,14 +285,14 @@ static void handle_request(int fd) { kill(request.pid, SIGSTOP); // don't dump sibling threads when attaching to GDB because it // makes the process less reliable, apparently... - tombstone_path = engrave_tombstone( - request.pid, request.tid, signal, request.abort_msg_address, !attach_gdb, - false, &detach_failed, &total_sleep_time_usec); + tombstone_path = engrave_tombstone(request.pid, request.tid, + signal, request.original_si_code, + request.abort_msg_address, !attach_gdb, + &detach_failed, &total_sleep_time_usec); break; default: - XLOG("stopped -- unexpected signal\n"); - LOG("process stopped due to unexpected signal %d\n", signal); + ALOGE("process stopped due to unexpected signal %d\n", signal); break; } break; @@ -358,14 +308,14 @@ static void handle_request(int fd) { free(tombstone_path); } - XLOG("detaching\n"); + ALOGV("detaching\n"); if (attach_gdb) { // stop the process so we can debug kill(request.pid, SIGSTOP); // detach so we can attach gdbserver if (ptrace(PTRACE_DETACH, request.tid, 0, 0)) { - LOG("ptrace detach from %d failed: %s\n", request.tid, strerror(errno)); + ALOGE("ptrace detach from %d failed: %s\n", request.tid, strerror(errno)); detach_failed = true; } @@ -373,11 +323,11 @@ static void handle_request(int fd) { // for user action for the crashing process. // in this case, we log a message and turn the debug LED on // waiting for a gdb connection (for instance) - wait_for_user_action(request.pid); + wait_for_user_action(request); } else { // just detach if (ptrace(PTRACE_DETACH, request.tid, 0, 0)) { - LOG("ptrace detach from %d failed: %s\n", request.tid, strerror(errno)); + ALOGE("ptrace detach from %d failed: %s\n", request.tid, strerror(errno)); detach_failed = true; } } @@ -389,7 +339,7 @@ static void handle_request(int fd) { // actual parent won't receive a death notification via wait(2). At this point // there's not much we can do about that. if (detach_failed) { - LOG("debuggerd committing suicide to free the zombie!\n"); + ALOGE("debuggerd committing suicide to free the zombie!\n"); kill(getpid(), SIGKILL); } } @@ -401,54 +351,50 @@ static void handle_request(int fd) { } static int do_server() { - int s; - struct sigaction act; - int logsocket = -1; - - // debuggerd crashes can't be reported to debuggerd. Reset all of the - // crash handlers. - signal(SIGILL, SIG_DFL); + // debuggerd crashes can't be reported to debuggerd. + // Reset all of the crash handlers. signal(SIGABRT, SIG_DFL); signal(SIGBUS, SIG_DFL); signal(SIGFPE, SIG_DFL); + signal(SIGILL, SIG_DFL); signal(SIGSEGV, SIG_DFL); #ifdef SIGSTKFLT signal(SIGSTKFLT, SIG_DFL); #endif + signal(SIGTRAP, SIG_DFL); // Ignore failed writes to closed sockets signal(SIGPIPE, SIG_IGN); - logsocket = socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_DGRAM); + int logsocket = socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_DGRAM); if (logsocket < 0) { logsocket = -1; } else { fcntl(logsocket, F_SETFD, FD_CLOEXEC); } + struct sigaction act; act.sa_handler = SIG_DFL; sigemptyset(&act.sa_mask); sigaddset(&act.sa_mask,SIGCHLD); act.sa_flags = SA_NOCLDWAIT; sigaction(SIGCHLD, &act, 0); - s = socket_local_server(DEBUGGER_SOCKET_NAME, ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM); + int s = socket_local_server(DEBUGGER_SOCKET_NAME, ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM); if (s < 0) return 1; fcntl(s, F_SETFD, FD_CLOEXEC); - LOG("debuggerd: " __DATE__ " " __TIME__ "\n"); + ALOGI("debuggerd: " __DATE__ " " __TIME__ "\n"); for (;;) { - struct sockaddr addr; - socklen_t alen; - int fd; + sockaddr addr; + socklen_t alen = sizeof(addr); - alen = sizeof(addr); - XLOG("waiting for connection\n"); - fd = accept(s, &addr, &alen); + ALOGV("waiting for connection\n"); + int fd = accept(s, &addr, &alen); if (fd < 0) { - XLOG("accept failed: %s\n", strerror(errno)); + ALOGV("accept failed: %s\n", strerror(errno)); continue; } diff --git a/debuggerd/machine.h b/debuggerd/machine.h index 2f1e201..fca9fbe 100644 --- a/debuggerd/machine.h +++ b/debuggerd/machine.h @@ -21,7 +21,7 @@ #include "utility.h" -void dump_memory_and_code(log_t* log, pid_t tid, int scope_flags); -void dump_registers(log_t* log, pid_t tid, int scope_flags); +void dump_memory_and_code(log_t* log, pid_t tid); +void dump_registers(log_t* log, pid_t tid); #endif // _DEBUGGERD_MACHINE_H diff --git a/debuggerd/mips/machine.cpp b/debuggerd/mips/machine.cpp index d1a7f2d..97834c7 100644 --- a/debuggerd/mips/machine.cpp +++ b/debuggerd/mips/machine.cpp @@ -22,141 +22,91 @@ #include <sys/types.h> #include <sys/ptrace.h> -#include <corkscrew/ptrace.h> - #include <sys/user.h> #include "../utility.h" #include "../machine.h" -// enable to dump memory pointed to by every register -#define DUMP_MEMORY_FOR_ALL_REGISTERS 1 - #define R(x) (static_cast<unsigned int>(x)) -static void dump_memory(log_t* log, pid_t tid, uintptr_t addr, int scope_flags) { - char code_buffer[64]; // actual 8+1+((8+1)*4) + 1 == 45 - char ascii_buffer[32]; // actual 16 + 1 == 17 - uintptr_t p, end; - - p = addr & ~3; - p -= 32; - if (p > addr) { - // catch underflow - p = 0; - } - end = p + 80; - // catch overflow; 'end - p' has to be multiples of 16 - while (end < p) - end -= 16; - - // Dump the code around PC as: - // addr contents ascii - // 00008d34 ef000000 e8bd0090 e1b00000 512fff1e ............../Q - // 00008d44 ea00b1f9 e92d0090 e3a070fc ef000000 ......-..p...... - while (p < end) { - char* asc_out = ascii_buffer; - - sprintf(code_buffer, "%08x ", p); - - int i; - for (i = 0; i < 4; i++) { - // If we see (data == -1 && errno != 0), we know that the ptrace - // call failed, probably because we're dumping memory in an - // unmapped or inaccessible page. I don't know if there's - // value in making that explicit in the output -- it likely - // just complicates parsing and clarifies nothing for the - // enlightened reader. - long data = ptrace(PTRACE_PEEKTEXT, tid, (void*)p, NULL); - sprintf(code_buffer + strlen(code_buffer), "%08lx ", data); - - int j; - for (j = 0; j < 4; j++) { - // Our isprint() allows high-ASCII characters that display - // differently (often badly) in different viewers, so we - // just use a simpler test. - char val = (data >> (j*8)) & 0xff; - if (val >= 0x20 && val < 0x7f) { - *asc_out++ = val; - } else { - *asc_out++ = '.'; - } - } - p += 4; - } - *asc_out = '\0'; - _LOG(log, scope_flags, " %s %s\n", code_buffer, ascii_buffer); - } -} +// The MIPS uapi ptrace.h has the wrong definition for pt_regs. PTRACE_GETREGS +// writes 64-bit quantities even though the public struct uses 32-bit ones. +struct pt_regs_mips_t { + uint64_t regs[32]; + uint64_t lo; + uint64_t hi; + uint64_t cp0_epc; + uint64_t cp0_badvaddr; + uint64_t cp0_status; + uint64_t cp0_cause; +}; // If configured to do so, dump memory around *all* registers // for the crashing thread. -void dump_memory_and_code(log_t* log, pid_t tid, int scope_flags) { +void dump_memory_and_code(log_t* log, pid_t tid) { pt_regs_mips_t r; if (ptrace(PTRACE_GETREGS, tid, 0, &r)) { return; } - if (IS_AT_FAULT(scope_flags) && DUMP_MEMORY_FOR_ALL_REGISTERS) { - static const char REG_NAMES[] = "$0atv0v1a0a1a2a3t0t1t2t3t4t5t6t7s0s1s2s3s4s5s6s7t8t9k0k1gpsps8ra"; + static const char REG_NAMES[] = "$0atv0v1a0a1a2a3t0t1t2t3t4t5t6t7s0s1s2s3s4s5s6s7t8t9k0k1gpsps8ra"; - for (int reg = 0; reg < 32; reg++) { - // skip uninteresting registers - if (reg == 0 // $0 - || reg == 26 // $k0 - || reg == 27 // $k1 - || reg == 31 // $ra (done below) - ) - continue; + for (int reg = 0; reg < 32; reg++) { + // skip uninteresting registers + if (reg == 0 // $0 + || reg == 26 // $k0 + || reg == 27 // $k1 + || reg == 31 // $ra (done below) + ) + continue; - uintptr_t addr = R(r.regs[reg]); + uintptr_t addr = R(r.regs[reg]); - // Don't bother if it looks like a small int or ~= null, or if - // it's in the kernel area. - if (addr < 4096 || addr >= 0x80000000) { - continue; - } - - _LOG(log, scope_flags | SCOPE_SENSITIVE, "\nmemory near %.2s:\n", ®_NAMES[reg * 2]); - dump_memory(log, tid, addr, scope_flags | SCOPE_SENSITIVE); + // Don't bother if it looks like a small int or ~= null, or if + // it's in the kernel area. + if (addr < 4096 || addr >= 0x80000000) { + continue; } + + _LOG(log, logtype::MEMORY, "\nmemory near %.2s:\n", ®_NAMES[reg * 2]); + dump_memory(log, tid, addr); } unsigned int pc = R(r.cp0_epc); unsigned int ra = R(r.regs[31]); - _LOG(log, scope_flags, "\ncode around pc:\n"); - dump_memory(log, tid, (uintptr_t)pc, scope_flags); + _LOG(log, logtype::MEMORY, "\ncode around pc:\n"); + dump_memory(log, tid, (uintptr_t)pc); if (pc != ra) { - _LOG(log, scope_flags, "\ncode around ra:\n"); - dump_memory(log, tid, (uintptr_t)ra, scope_flags); + _LOG(log, logtype::MEMORY, "\ncode around ra:\n"); + dump_memory(log, tid, (uintptr_t)ra); } } -void dump_registers(log_t* log, pid_t tid, int scope_flags) { +void dump_registers(log_t* log, pid_t tid) { pt_regs_mips_t r; if(ptrace(PTRACE_GETREGS, tid, 0, &r)) { - _LOG(log, scope_flags, "cannot get registers: %s\n", strerror(errno)); + _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno)); return; } - _LOG(log, scope_flags, " zr %08x at %08x v0 %08x v1 %08x\n", + _LOG(log, logtype::REGISTERS, " zr %08x at %08x v0 %08x v1 %08x\n", R(r.regs[0]), R(r.regs[1]), R(r.regs[2]), R(r.regs[3])); - _LOG(log, scope_flags, " a0 %08x a1 %08x a2 %08x a3 %08x\n", + _LOG(log, logtype::REGISTERS, " a0 %08x a1 %08x a2 %08x a3 %08x\n", R(r.regs[4]), R(r.regs[5]), R(r.regs[6]), R(r.regs[7])); - _LOG(log, scope_flags, " t0 %08x t1 %08x t2 %08x t3 %08x\n", + _LOG(log, logtype::REGISTERS, " t0 %08x t1 %08x t2 %08x t3 %08x\n", R(r.regs[8]), R(r.regs[9]), R(r.regs[10]), R(r.regs[11])); - _LOG(log, scope_flags, " t4 %08x t5 %08x t6 %08x t7 %08x\n", + _LOG(log, logtype::REGISTERS, " t4 %08x t5 %08x t6 %08x t7 %08x\n", R(r.regs[12]), R(r.regs[13]), R(r.regs[14]), R(r.regs[15])); - _LOG(log, scope_flags, " s0 %08x s1 %08x s2 %08x s3 %08x\n", + _LOG(log, logtype::REGISTERS, " s0 %08x s1 %08x s2 %08x s3 %08x\n", R(r.regs[16]), R(r.regs[17]), R(r.regs[18]), R(r.regs[19])); - _LOG(log, scope_flags, " s4 %08x s5 %08x s6 %08x s7 %08x\n", + _LOG(log, logtype::REGISTERS, " s4 %08x s5 %08x s6 %08x s7 %08x\n", R(r.regs[20]), R(r.regs[21]), R(r.regs[22]), R(r.regs[23])); - _LOG(log, scope_flags, " t8 %08x t9 %08x k0 %08x k1 %08x\n", + _LOG(log, logtype::REGISTERS, " t8 %08x t9 %08x k0 %08x k1 %08x\n", R(r.regs[24]), R(r.regs[25]), R(r.regs[26]), R(r.regs[27])); - _LOG(log, scope_flags, " gp %08x sp %08x s8 %08x ra %08x\n", + _LOG(log, logtype::REGISTERS, " gp %08x sp %08x s8 %08x ra %08x\n", R(r.regs[28]), R(r.regs[29]), R(r.regs[30]), R(r.regs[31])); - _LOG(log, scope_flags, " hi %08x lo %08x bva %08x epc %08x\n", + _LOG(log, logtype::REGISTERS, " hi %08x lo %08x bva %08x epc %08x\n", R(r.hi), R(r.lo), R(r.cp0_badvaddr), R(r.cp0_epc)); } diff --git a/debuggerd/tombstone.cpp b/debuggerd/tombstone.cpp index c9a2376..f41166b 100644..100755 --- a/debuggerd/tombstone.cpp +++ b/debuggerd/tombstone.cpp @@ -14,31 +14,33 @@ * limitations under the License. */ +#define LOG_TAG "DEBUG" + +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <inttypes.h> +#include <signal.h> #include <stddef.h> +#include <stdio.h> #include <stdlib.h> -#include <signal.h> #include <string.h> -#include <stdio.h> -#include <fcntl.h> -#include <errno.h> -#include <dirent.h> #include <time.h> #include <sys/ptrace.h> +#include <sys/socket.h> #include <sys/stat.h> -#include <inttypes.h> +#include <sys/un.h> #include <private/android_filesystem_config.h> +#include <cutils/properties.h> #include <log/log.h> #include <log/logger.h> -#include <cutils/properties.h> +#include <log/logprint.h> #include <backtrace/Backtrace.h> #include <backtrace/BacktraceMap.h> -#include <sys/socket.h> -#include <linux/un.h> - #include <selinux/android.h> #include <UniquePtr.h> @@ -51,22 +53,17 @@ #define MAX_TOMBSTONES 10 #define TOMBSTONE_DIR "/data/tombstones" +#define TOMBSTONE_TEMPLATE (TOMBSTONE_DIR"/tombstone_%02d") // Must match the path defined in NativeCrashListener.java #define NCRASH_SOCKET_PATH "/data/system/ndebugsocket" -#define typecheck(x,y) { \ - typeof(x) __dummy1; \ - typeof(y) __dummy2; \ - (void)(&__dummy1 == &__dummy2); } - - -static bool signal_has_address(int sig) { +static bool signal_has_si_addr(int sig) { switch (sig) { - case SIGILL: + case SIGBUS: case SIGFPE: + case SIGILL: case SIGSEGV: - case SIGBUS: return true; default: return false; @@ -75,16 +72,17 @@ static bool signal_has_address(int sig) { static const char* get_signame(int sig) { switch(sig) { - case SIGILL: return "SIGILL"; case SIGABRT: return "SIGABRT"; case SIGBUS: return "SIGBUS"; case SIGFPE: return "SIGFPE"; - case SIGSEGV: return "SIGSEGV"; + case SIGILL: return "SIGILL"; case SIGPIPE: return "SIGPIPE"; -#ifdef SIGSTKFLT + case SIGSEGV: return "SIGSEGV"; +#if defined(SIGSTKFLT) case SIGSTKFLT: return "SIGSTKFLT"; #endif case SIGSTOP: return "SIGSTOP"; + case SIGTRAP: return "SIGTRAP"; default: return "?"; } } @@ -103,13 +101,17 @@ static const char* get_sigcode(int signo, int code) { case ILL_COPROC: return "ILL_COPROC"; case ILL_BADSTK: return "ILL_BADSTK"; } + static_assert(NSIGILL == ILL_BADSTK, "missing ILL_* si_code"); break; case SIGBUS: switch (code) { case BUS_ADRALN: return "BUS_ADRALN"; case BUS_ADRERR: return "BUS_ADRERR"; case BUS_OBJERR: return "BUS_OBJERR"; + case BUS_MCEERR_AR: return "BUS_MCEERR_AR"; + case BUS_MCEERR_AO: return "BUS_MCEERR_AO"; } + static_assert(NSIGBUS == BUS_MCEERR_AO, "missing BUS_* si_code"); break; case SIGFPE: switch (code) { @@ -122,74 +124,76 @@ static const char* get_sigcode(int signo, int code) { case FPE_FLTINV: return "FPE_FLTINV"; case FPE_FLTSUB: return "FPE_FLTSUB"; } + static_assert(NSIGFPE == FPE_FLTSUB, "missing FPE_* si_code"); break; case SIGSEGV: switch (code) { case SEGV_MAPERR: return "SEGV_MAPERR"; case SEGV_ACCERR: return "SEGV_ACCERR"; } + static_assert(NSIGSEGV == SEGV_ACCERR, "missing SEGV_* si_code"); break; case SIGTRAP: switch (code) { case TRAP_BRKPT: return "TRAP_BRKPT"; case TRAP_TRACE: return "TRAP_TRACE"; + case TRAP_BRANCH: return "TRAP_BRANCH"; + case TRAP_HWBKPT: return "TRAP_HWBKPT"; } + static_assert(NSIGTRAP == TRAP_HWBKPT, "missing TRAP_* si_code"); break; } // Then the other codes... switch (code) { case SI_USER: return "SI_USER"; -#if defined(SI_KERNEL) case SI_KERNEL: return "SI_KERNEL"; -#endif case SI_QUEUE: return "SI_QUEUE"; case SI_TIMER: return "SI_TIMER"; case SI_MESGQ: return "SI_MESGQ"; case SI_ASYNCIO: return "SI_ASYNCIO"; -#if defined(SI_SIGIO) case SI_SIGIO: return "SI_SIGIO"; -#endif -#if defined(SI_TKILL) case SI_TKILL: return "SI_TKILL"; -#endif + case SI_DETHREAD: return "SI_DETHREAD"; } // Then give up... return "?"; } -static void dump_revision_info(log_t* log) { - char revision[PROPERTY_VALUE_MAX]; - - property_get("ro.revision", revision, "unknown"); - - _LOG(log, SCOPE_AT_FAULT, "Revision: '%s'\n", revision); -} - -static void dump_build_info(log_t* log) { +static void dump_header_info(log_t* log) { char fingerprint[PROPERTY_VALUE_MAX]; + char revision[PROPERTY_VALUE_MAX]; property_get("ro.build.fingerprint", fingerprint, "unknown"); + property_get("ro.revision", revision, "unknown"); - _LOG(log, SCOPE_AT_FAULT, "Build fingerprint: '%s'\n", fingerprint); + _LOG(log, logtype::HEADER, "Build fingerprint: '%s'\n", fingerprint); + _LOG(log, logtype::HEADER, "Revision: '%s'\n", revision); + _LOG(log, logtype::HEADER, "ABI: '%s'\n", ABI_STRING); } -static void dump_fault_addr(log_t* log, pid_t tid, int sig) { +static void dump_signal_info(log_t* log, pid_t tid, int signal, int si_code) { siginfo_t si; - memset(&si, 0, sizeof(si)); - if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si)){ - _LOG(log, SCOPE_AT_FAULT, "cannot get siginfo: %s\n", strerror(errno)); - } else if (signal_has_address(sig)) { - _LOG(log, SCOPE_AT_FAULT, "signal %d (%s), code %d (%s), fault addr %0*" PRIxPTR "\n", - sig, get_signame(sig), si.si_code, get_sigcode(sig, si.si_code), - sizeof(uintptr_t)*2, reinterpret_cast<uintptr_t>(si.si_addr)); + if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si) == -1) { + _LOG(log, logtype::HEADER, "cannot get siginfo: %s\n", strerror(errno)); + return; + } + + // bionic has to re-raise some signals, which overwrites the si_code with SI_TKILL. + si.si_code = si_code; + + char addr_desc[32]; // ", fault addr 0x1234" + if (signal_has_si_addr(signal)) { + snprintf(addr_desc, sizeof(addr_desc), "%p", si.si_addr); } else { - _LOG(log, SCOPE_AT_FAULT, "signal %d (%s), code %d (%s), fault addr --------\n", - sig, get_signame(sig), si.si_code, get_sigcode(sig, si.si_code)); + snprintf(addr_desc, sizeof(addr_desc), "--------"); } + + _LOG(log, logtype::HEADER, "signal %d (%s), code %d (%s), fault addr %s\n", + signal, get_signame(signal), si.si_code, get_sigcode(signal, si.si_code), addr_desc); } -static void dump_thread_info(log_t* log, pid_t pid, pid_t tid, int scope_flags) { +static void dump_thread_info(log_t* log, pid_t pid, pid_t tid) { char path[64]; char threadnamebuf[1024]; char* threadname = NULL; @@ -207,27 +211,23 @@ static void dump_thread_info(log_t* log, pid_t pid, pid_t tid, int scope_flags) } } - if (IS_AT_FAULT(scope_flags)) { - char procnamebuf[1024]; - char* procname = NULL; - - snprintf(path, sizeof(path), "/proc/%d/cmdline", pid); - if ((fp = fopen(path, "r"))) { - procname = fgets(procnamebuf, sizeof(procnamebuf), fp); - fclose(fp); - } + char procnamebuf[1024]; + char* procname = NULL; - _LOG(log, SCOPE_AT_FAULT, "pid: %d, tid: %d, name: %s >>> %s <<<\n", pid, tid, - threadname ? threadname : "UNKNOWN", procname ? procname : "UNKNOWN"); - } else { - _LOG(log, 0, "pid: %d, tid: %d, name: %s\n", pid, tid, threadname ? threadname : "UNKNOWN"); + snprintf(path, sizeof(path), "/proc/%d/cmdline", pid); + if ((fp = fopen(path, "r"))) { + procname = fgets(procnamebuf, sizeof(procnamebuf), fp); + fclose(fp); } + + _LOG(log, logtype::HEADER, "pid: %d, tid: %d, name: %s >>> %s <<<\n", pid, tid, + threadname ? threadname : "UNKNOWN", procname ? procname : "UNKNOWN"); } static void dump_stack_segment( - Backtrace* backtrace, log_t* log, int scope_flags, uintptr_t* sp, size_t words, int label) { + Backtrace* backtrace, log_t* log, uintptr_t* sp, size_t words, int label) { for (size_t i = 0; i < words; i++) { - uint32_t stack_content; + word_t stack_content; if (!backtrace->ReadWord(*sp, &stack_content)) { break; } @@ -244,36 +244,36 @@ static void dump_stack_segment( if (!func_name.empty()) { if (!i && label >= 0) { if (offset) { - _LOG(log, scope_flags, " #%02d %08x %08x %s (%s+%u)\n", + _LOG(log, logtype::STACK, " #%02d %" PRIPTR " %" PRIPTR " %s (%s+%" PRIuPTR ")\n", label, *sp, stack_content, map_name, func_name.c_str(), offset); } else { - _LOG(log, scope_flags, " #%02d %08x %08x %s (%s)\n", + _LOG(log, logtype::STACK, " #%02d %" PRIPTR " %" PRIPTR " %s (%s)\n", label, *sp, stack_content, map_name, func_name.c_str()); } } else { if (offset) { - _LOG(log, scope_flags, " %08x %08x %s (%s+%u)\n", + _LOG(log, logtype::STACK, " %" PRIPTR " %" PRIPTR " %s (%s+%" PRIuPTR ")\n", *sp, stack_content, map_name, func_name.c_str(), offset); } else { - _LOG(log, scope_flags, " %08x %08x %s (%s)\n", + _LOG(log, logtype::STACK, " %" PRIPTR " %" PRIPTR " %s (%s)\n", *sp, stack_content, map_name, func_name.c_str()); } } } else { if (!i && label >= 0) { - _LOG(log, scope_flags, " #%02d %08x %08x %s\n", + _LOG(log, logtype::STACK, " #%02d %" PRIPTR " %" PRIPTR " %s\n", label, *sp, stack_content, map_name); } else { - _LOG(log, scope_flags, " %08x %08x %s\n", + _LOG(log, logtype::STACK, " %" PRIPTR " %" PRIPTR " %s\n", *sp, stack_content, map_name); } } - *sp += sizeof(uint32_t); + *sp += sizeof(word_t); } } -static void dump_stack(Backtrace* backtrace, log_t* log, int scope_flags) { +static void dump_stack(Backtrace* backtrace, log_t* log) { size_t first = 0, last; for (size_t i = 0; i < backtrace->NumFrames(); i++) { const backtrace_frame_data_t* frame = backtrace->GetFrame(i); @@ -289,127 +289,111 @@ static void dump_stack(Backtrace* backtrace, log_t* log, int scope_flags) { } first--; - scope_flags |= SCOPE_SENSITIVE; - // Dump a few words before the first frame. - uintptr_t sp = backtrace->GetFrame(first)->sp - STACK_WORDS * sizeof(uint32_t); - dump_stack_segment(backtrace, log, scope_flags, &sp, STACK_WORDS, -1); + word_t sp = backtrace->GetFrame(first)->sp - STACK_WORDS * sizeof(word_t); + dump_stack_segment(backtrace, log, &sp, STACK_WORDS, -1); // Dump a few words from all successive frames. // Only log the first 3 frames, put the rest in the tombstone. for (size_t i = first; i <= last; i++) { const backtrace_frame_data_t* frame = backtrace->GetFrame(i); if (sp != frame->sp) { - _LOG(log, scope_flags, " ........ ........\n"); + _LOG(log, logtype::STACK, " ........ ........\n"); sp = frame->sp; } - if (i - first == 3) { - scope_flags &= (~SCOPE_AT_FAULT); - } if (i == last) { - dump_stack_segment(backtrace, log, scope_flags, &sp, STACK_WORDS, i); + dump_stack_segment(backtrace, log, &sp, STACK_WORDS, i); if (sp < frame->sp + frame->stack_size) { - _LOG(log, scope_flags, " ........ ........\n"); + _LOG(log, logtype::STACK, " ........ ........\n"); } } else { - size_t words = frame->stack_size / sizeof(uint32_t); + size_t words = frame->stack_size / sizeof(word_t); if (words == 0) { words = 1; } else if (words > STACK_WORDS) { words = STACK_WORDS; } - dump_stack_segment(backtrace, log, scope_flags, &sp, words, i); + dump_stack_segment(backtrace, log, &sp, words, i); } } } -static void dump_backtrace_and_stack(Backtrace* backtrace, log_t* log, int scope_flags) { +static void dump_backtrace_and_stack(Backtrace* backtrace, log_t* log) { if (backtrace->NumFrames()) { - _LOG(log, scope_flags, "\nbacktrace:\n"); - dump_backtrace_to_log(backtrace, log, scope_flags, " "); + _LOG(log, logtype::BACKTRACE, "\nbacktrace:\n"); + dump_backtrace_to_log(backtrace, log, " "); - _LOG(log, scope_flags, "\nstack:\n"); - dump_stack(backtrace, log, scope_flags); + _LOG(log, logtype::STACK, "\nstack:\n"); + dump_stack(backtrace, log); } } -static void dump_map(log_t* log, const backtrace_map_t* map, const char* what, int scope_flags) { - if (map != NULL) { - _LOG(log, scope_flags, " %08x-%08x %c%c%c %s\n", map->start, map->end, +static void dump_map(log_t* log, const backtrace_map_t* map, bool fault_addr) { + _LOG(log, logtype::MAPS, "%s%" PRIPTR "-%" PRIPTR " %c%c%c %7" PRIdPTR " %s\n", + (fault_addr? "--->" : " "), map->start, map->end - 1, (map->flags & PROT_READ) ? 'r' : '-', (map->flags & PROT_WRITE) ? 'w' : '-', - (map->flags & PROT_EXEC) ? 'x' : '-', map->name.c_str()); - } else { - _LOG(log, scope_flags, " (no %s)\n", what); - } + (map->flags & PROT_EXEC) ? 'x' : '-', + (map->end - map->start), map->name.c_str()); } -static void dump_nearby_maps(BacktraceMap* map, log_t* log, pid_t tid, int scope_flags) { - scope_flags |= SCOPE_SENSITIVE; +static void dump_nearby_maps(BacktraceMap* map, log_t* log, pid_t tid) { siginfo_t si; memset(&si, 0, sizeof(si)); if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si)) { - _LOG(log, scope_flags, "cannot get siginfo for %d: %s\n", tid, strerror(errno)); - return; - } - if (!signal_has_address(si.si_signo)) { + _LOG(log, logtype::MAPS, "cannot get siginfo for %d: %s\n", tid, strerror(errno)); return; } + bool is_running = (si.si_code == SI_USER); uintptr_t addr = reinterpret_cast<uintptr_t>(si.si_addr); addr &= ~0xfff; // round to 4K page boundary - if (addr == 0) { // null-pointer deref + if (!is_running && addr == 0) { // null-pointer deref return; } - _LOG(log, scope_flags, "\nmemory map around fault addr %" PRIxPTR ":\n", - reinterpret_cast<uintptr_t>(si.si_addr)); + _LOG(log, logtype::MAPS, "\nmemory map: %s\n", is_running? "" : "(fault address prefixed with --->)"); - // Search for a match, or for a hole where the match would be. The list - // is backward from the file content, so it starts at high addresses. - const backtrace_map_t* cur_map = NULL; - const backtrace_map_t* next_map = NULL; - const backtrace_map_t* prev_map = NULL; + if(!is_running && (addr < map->begin()->start)) { + _LOG(log, logtype::MAPS, "--->Fault address falls at %" PRIPTR " before any mapped regions\n", addr); + } + + BacktraceMap::const_iterator prev = map->begin(); for (BacktraceMap::const_iterator it = map->begin(); it != map->end(); ++it) { - if (addr >= it->start && addr < it->end) { - cur_map = &*it; - if (it != map->begin()) { - prev_map = &*(it-1); - } - if (++it != map->end()) { - next_map = &*it; - } - break; + if (addr >= (*prev).end && addr < (*it).start) { + _LOG(log, logtype::MAPS, "--->Fault address falls at %" PRIPTR " between mapped regions\n", addr); } + prev = it; + bool in_map = !is_running && (addr >= (*it).start) && (addr < (*it).end); + dump_map(log, &*it, in_map); + } + if (!is_running && (addr >= (*prev).end)) { + _LOG(log, logtype::MAPS, "--->Fault address falls at %" PRIPTR " after any mapped regions\n", addr); } - - // Show the map address in ascending order (like /proc/pid/maps). - dump_map(log, prev_map, "map below", scope_flags); - dump_map(log, cur_map, "map for address", scope_flags); - dump_map(log, next_map, "map above", scope_flags); } static void dump_thread( - Backtrace* backtrace, log_t* log, int scope_flags, int* total_sleep_time_usec) { + Backtrace* backtrace, log_t* log, int* total_sleep_time_usec) { + wait_for_stop(backtrace->Tid(), total_sleep_time_usec); - dump_registers(log, backtrace->Tid(), scope_flags); - dump_backtrace_and_stack(backtrace, log, scope_flags); - if (IS_AT_FAULT(scope_flags)) { - dump_memory_and_code(log, backtrace->Tid(), scope_flags); - dump_nearby_maps(backtrace->GetMap(), log, backtrace->Tid(), scope_flags); - } + dump_registers(log, backtrace->Tid()); + dump_backtrace_and_stack(backtrace, log); + + dump_memory_and_code(log, backtrace->Tid()); + dump_nearby_maps(backtrace->GetMap(), log, backtrace->Tid()); } // Return true if some thread is not detached cleanly static bool dump_sibling_thread_report( log_t* log, pid_t pid, pid_t tid, int* total_sleep_time_usec, BacktraceMap* map) { char task_path[64]; + snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid); DIR* d = opendir(task_path); // Bail early if the task directory cannot be opened if (d == NULL) { - XLOG("Cannot open /proc/%d/task\n", pid); + ALOGE("Cannot open /proc/%d/task\n", pid); return false; } @@ -430,19 +414,23 @@ static bool dump_sibling_thread_report( // Skip this thread if cannot ptrace it if (ptrace(PTRACE_ATTACH, new_tid, 0, 0) < 0) { + _LOG(log, logtype::ERROR, "ptrace attach to %d failed: %s\n", new_tid, strerror(errno)); continue; } - _LOG(log, 0, "--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n"); - dump_thread_info(log, pid, new_tid, 0); + log->current_tid = new_tid; + _LOG(log, logtype::THREAD, "--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n"); + dump_thread_info(log, pid, new_tid); UniquePtr<Backtrace> backtrace(Backtrace::Create(pid, new_tid, map)); if (backtrace->Unwind(0)) { - dump_thread(backtrace.get(), log, 0, total_sleep_time_usec); + dump_thread(backtrace.get(), log, total_sleep_time_usec); } + log->current_tid = log->crashed_tid; + if (ptrace(PTRACE_DETACH, new_tid, 0, 0) != 0) { - LOG("ptrace detach from %d failed: %s\n", new_tid, strerror(errno)); + _LOG(log, logtype::ERROR, "ptrace detach from %d failed: %s\n", new_tid, strerror(errno)); detach_failed = true; } } @@ -454,17 +442,19 @@ static bool dump_sibling_thread_report( // Reads the contents of the specified log device, filters out the entries // that don't match the specified pid, and writes them to the tombstone file. // -// If "tail" is set, we only print the last few lines. -static void dump_log_file(log_t* log, pid_t pid, const char* filename, - unsigned int tail) { +// If "tail" is non-zero, log the last "tail" number of lines. +static EventTagMap* g_eventTagMap = NULL; + +static void dump_log_file( + log_t* log, pid_t pid, const char* filename, unsigned int tail) { bool first = true; - struct logger_list *logger_list; + struct logger_list* logger_list; logger_list = android_logger_list_open( - android_name_to_log_id(filename), O_RDONLY | O_NONBLOCK, tail, pid); + android_name_to_log_id(filename), O_RDONLY | O_NONBLOCK, tail, pid); if (!logger_list) { - XLOG("Unable to open %s: %s\n", filename, strerror(errno)); + ALOGE("Unable to open %s: %s\n", filename, strerror(errno)); return; } @@ -472,6 +462,7 @@ static void dump_log_file(log_t* log, pid_t pid, const char* filename, while (true) { ssize_t actual = android_logger_list_read(logger_list, &log_entry); + struct logger_entry* entry; if (actual < 0) { if (actual == -EINTR) { @@ -481,29 +472,25 @@ static void dump_log_file(log_t* log, pid_t pid, const char* filename, // non-blocking EOF; we're done break; } else { - _LOG(log, 0, "Error while reading log: %s\n", + _LOG(log, logtype::ERROR, "Error while reading log: %s\n", strerror(-actual)); break; } } else if (actual == 0) { - _LOG(log, 0, "Got zero bytes while reading log: %s\n", + _LOG(log, logtype::ERROR, "Got zero bytes while reading log: %s\n", strerror(errno)); break; } - // NOTE: if you XLOG something here, this will spin forever, + // NOTE: if you ALOGV something here, this will spin forever, // because you will be writing as fast as you're reading. Any // high-frequency debug diagnostics should just be written to // the tombstone file. - struct logger_entry* entry = &log_entry.entry_v1; - if (entry->pid != static_cast<int32_t>(pid)) { - // wrong pid, ignore - continue; - } + entry = &log_entry.entry_v1; if (first) { - _LOG(log, 0, "--------- %slog %s\n", + _LOG(log, logtype::LOGS, "--------- %slog %s\n", tail ? "tail end of " : "", filename); first = false; } @@ -517,7 +504,28 @@ static void dump_log_file(log_t* log, pid_t pid, const char* filename, if (!hdr_size) { hdr_size = sizeof(log_entry.entry_v1); } - char* msg = (char *)log_entry.buf + hdr_size; + char* msg = reinterpret_cast<char*>(log_entry.buf) + hdr_size; + + char timeBuf[32]; + time_t sec = static_cast<time_t>(entry->sec); + struct tm tmBuf; + struct tm* ptm; + ptm = localtime_r(&sec, &tmBuf); + strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm); + + if (log_entry.id() == LOG_ID_EVENTS) { + if (!g_eventTagMap) { + g_eventTagMap = android_openEventTagMap(EVENT_TAG_MAP_FILE); + } + AndroidLogEntry e; + char buf[512]; + android_log_processBinaryLogBuffer(entry, &e, g_eventTagMap, buf, sizeof(buf)); + _LOG(log, logtype::LOGS, "%s.%03d %5d %5d %c %-8s: %s\n", + timeBuf, entry->nsec / 1000000, entry->pid, entry->tid, + 'I', e.tag, e.message); + continue; + } + unsigned char prio = msg[0]; char* tag = msg + 1; msg = tag + strlen(tag) + 1; @@ -525,18 +533,11 @@ static void dump_log_file(log_t* log, pid_t pid, const char* filename, // consume any trailing newlines char* nl = msg + strlen(msg) - 1; while (nl >= msg && *nl == '\n') { - *nl-- = '\0'; + *nl-- = '\0'; } char prioChar = (prio < strlen(kPrioChars) ? kPrioChars[prio] : '?'); - char timeBuf[32]; - time_t sec = static_cast<time_t>(entry->sec); - struct tm tmBuf; - struct tm* ptm; - ptm = localtime_r(&sec, &tmBuf); - strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm); - // Look for line breaks ('\n') and display each text line // on a separate line, prefixed with the header, like logcat does. do { @@ -546,10 +547,9 @@ static void dump_log_file(log_t* log, pid_t pid, const char* filename, ++nl; } - _LOG(log, 0, "%s.%03d %5d %5d %c %-8s: %s\n", + _LOG(log, logtype::LOGS, "%s.%03d %5d %5d %c %-8s: %s\n", timeBuf, entry->nsec / 1000000, entry->pid, entry->tid, prioChar, tag, msg); - } while ((msg = nl)); } @@ -558,7 +558,7 @@ static void dump_log_file(log_t* log, pid_t pid, const char* filename, // Dumps the logs generated by the specified pid to the tombstone, from both // "system" and "main" log devices. Ideally we'd interleave the output. -static void dump_logs(log_t* log, pid_t pid, unsigned tail) { +static void dump_logs(log_t* log, pid_t pid, unsigned int tail) { dump_log_file(log, pid, "system", tail); dump_log_file(log, pid, "main", tail); } @@ -574,33 +574,25 @@ static void dump_abort_message(Backtrace* backtrace, log_t* log, uintptr_t addre memset(msg, 0, sizeof(msg)); char* p = &msg[0]; while (p < &msg[sizeof(msg)]) { - uint32_t data; + word_t data; + size_t len = sizeof(word_t); if (!backtrace->ReadWord(address, &data)) { break; } - address += sizeof(uint32_t); + address += sizeof(word_t); - if ((*p++ = (data >> 0) & 0xff) == 0) { - break; - } - if ((*p++ = (data >> 8) & 0xff) == 0) { - break; - } - if ((*p++ = (data >> 16) & 0xff) == 0) { - break; - } - if ((*p++ = (data >> 24) & 0xff) == 0) { - break; - } + while (len > 0 && (*p++ = (data >> (sizeof(word_t) - len) * 8) & 0xff) != 0) + len--; } msg[sizeof(msg) - 1] = '\0'; - _LOG(log, SCOPE_AT_FAULT, "Abort message: '%s'\n", msg); + _LOG(log, logtype::HEADER, "Abort message: '%s'\n", msg); } // Dumps all information about the specified pid to the tombstone. -static bool dump_crash(log_t* log, pid_t pid, pid_t tid, int signal, uintptr_t abort_msg_address, - bool dump_sibling_threads, int* total_sleep_time_usec) { +static bool dump_crash(log_t* log, pid_t pid, pid_t tid, int signal, int si_code, + uintptr_t abort_msg_address, bool dump_sibling_threads, + int* total_sleep_time_usec) { // don't copy log messages to tombstone unless this is a dev device char value[PROPERTY_VALUE_MAX]; property_get("ro.debuggable", value, "0"); @@ -616,20 +608,20 @@ static bool dump_crash(log_t* log, pid_t pid, pid_t tid, int signal, uintptr_t a TEMP_FAILURE_RETRY( write(log->amfd, &datum, 4) ); } - _LOG(log, SCOPE_AT_FAULT, + _LOG(log, logtype::HEADER, "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n"); - dump_build_info(log); - dump_revision_info(log); - dump_thread_info(log, pid, tid, SCOPE_AT_FAULT); + dump_header_info(log); + dump_thread_info(log, pid, tid); + if (signal) { - dump_fault_addr(log, tid, signal); + dump_signal_info(log, tid, signal, si_code); } UniquePtr<BacktraceMap> map(BacktraceMap::Create(pid)); UniquePtr<Backtrace> backtrace(Backtrace::Create(pid, tid, map.get())); if (backtrace->Unwind(0)) { dump_abort_message(backtrace.get(), log, abort_msg_address); - dump_thread(backtrace.get(), log, SCOPE_AT_FAULT, total_sleep_time_usec); + dump_thread(backtrace.get(), log, total_sleep_time_usec); } if (want_logs) { @@ -663,24 +655,19 @@ static bool dump_crash(log_t* log, pid_t pid, pid_t tid, int signal, uintptr_t a // // Returns the path of the tombstone file, allocated using malloc(). Caller must free() it. static char* find_and_open_tombstone(int* fd) { - unsigned long mtime = ULONG_MAX; - struct stat sb; - - // XXX: Our stat.st_mtime isn't time_t. If it changes, as it probably ought - // to, our logic breaks. This check will generate a warning if that happens. - typecheck(mtime, sb.st_mtime); - - // In a single wolf-like pass, find an available slot and, in case none + // In a single pass, find an available slot and, in case none // exist, find and record the least-recently-modified file. char path[128]; - int oldest = 0; + int oldest = -1; + struct stat oldest_sb; for (int i = 0; i < MAX_TOMBSTONES; i++) { - snprintf(path, sizeof(path), TOMBSTONE_DIR"/tombstone_%02d", i); + snprintf(path, sizeof(path), TOMBSTONE_TEMPLATE, i); + struct stat sb; if (!stat(path, &sb)) { - if (sb.st_mtime < mtime) { + if (oldest < 0 || sb.st_mtime < oldest_sb.st_mtime) { oldest = i; - mtime = sb.st_mtime; + oldest_sb.st_mtime = sb.st_mtime; } continue; } @@ -695,11 +682,16 @@ static char* find_and_open_tombstone(int* fd) { return strdup(path); } + if (oldest < 0) { + ALOGE("Failed to find a valid tombstone, default to using tombstone 0.\n"); + oldest = 0; + } + // we didn't find an available file, so we clobber the oldest one - snprintf(path, sizeof(path), TOMBSTONE_DIR"/tombstone_%02d", oldest); + snprintf(path, sizeof(path), TOMBSTONE_TEMPLATE, oldest); *fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600); if (*fd < 0) { - LOG("failed to open tombstone file '%s': %s\n", path, strerror(errno)); + ALOGE("failed to open tombstone file '%s': %s\n", path, strerror(errno)); return NULL; } fchown(*fd, AID_SYSTEM, AID_SYSTEM); @@ -736,35 +728,45 @@ static int activity_manager_connect() { return amfd; } -char* engrave_tombstone( - pid_t pid, pid_t tid, int signal, uintptr_t abort_msg_address, bool dump_sibling_threads, - bool quiet, bool* detach_failed, int* total_sleep_time_usec) { - mkdir(TOMBSTONE_DIR, 0755); - chown(TOMBSTONE_DIR, AID_SYSTEM, AID_SYSTEM); +char* engrave_tombstone(pid_t pid, pid_t tid, int signal, int original_si_code, + uintptr_t abort_msg_address, bool dump_sibling_threads, + bool* detach_failed, int* total_sleep_time_usec) { + + log_t log; + log.current_tid = tid; + log.crashed_tid = tid; + + if ((mkdir(TOMBSTONE_DIR, 0755) == -1) && (errno != EEXIST)) { + _LOG(&log, logtype::ERROR, "failed to create %s: %s\n", TOMBSTONE_DIR, strerror(errno)); + } + + if (chown(TOMBSTONE_DIR, AID_SYSTEM, AID_SYSTEM) == -1) { + _LOG(&log, logtype::ERROR, "failed to change ownership of %s: %s\n", TOMBSTONE_DIR, strerror(errno)); + } int fd = -1; char* path = NULL; - if (selinux_android_restorecon(TOMBSTONE_DIR) == 0) { + if (selinux_android_restorecon(TOMBSTONE_DIR, 0) == 0) { path = find_and_open_tombstone(&fd); } else { - LOG("Failed to restore security context, not writing tombstone.\n"); + _LOG(&log, logtype::ERROR, "Failed to restore security context, not writing tombstone.\n"); } - if (fd < 0 && quiet) { - LOG("Skipping tombstone write, nothing to do.\n"); + if (fd < 0) { + _LOG(&log, logtype::ERROR, "Skipping tombstone write, nothing to do.\n"); *detach_failed = false; return NULL; } - log_t log; log.tfd = fd; // Preserve amfd since it can be modified through the calls below without // being closed. int amfd = activity_manager_connect(); log.amfd = amfd; - log.quiet = quiet; - *detach_failed = dump_crash( - &log, pid, tid, signal, abort_msg_address, dump_sibling_threads, total_sleep_time_usec); + *detach_failed = dump_crash(&log, pid, tid, signal, original_si_code, abort_msg_address, + dump_sibling_threads, total_sleep_time_usec); + + ALOGI("\nTombstone written to: %s\n", path); // Either of these file descriptors can be -1, any error is ignored. close(amfd); diff --git a/debuggerd/tombstone.h b/debuggerd/tombstone.h index e9878bf..7e2b2fe 100644 --- a/debuggerd/tombstone.h +++ b/debuggerd/tombstone.h @@ -23,7 +23,9 @@ /* Creates a tombstone file and writes the crash dump to it. * Returns the path of the tombstone, which must be freed using free(). */ -char* engrave_tombstone(pid_t pid, pid_t tid, int signal, uintptr_t abort_msg_address, - bool dump_sibling_threads, bool quiet, bool* detach_failed, int* total_sleep_time_usec); +char* engrave_tombstone(pid_t pid, pid_t tid, int signal, int original_si_code, + uintptr_t abort_msg_address, + bool dump_sibling_threads, bool* detach_failed, + int* total_sleep_time_usec); #endif // _DEBUGGERD_TOMBSTONE_H diff --git a/debuggerd/utility.cpp b/debuggerd/utility.cpp index 35f061e..a163344 100644 --- a/debuggerd/utility.cpp +++ b/debuggerd/utility.cpp @@ -14,19 +14,19 @@ * limitations under the License. */ -#include <stddef.h> -#include <stdio.h> -#include <string.h> +#define LOG_TAG "DEBUG" + +#include "utility.h" + #include <errno.h> -#include <unistd.h> #include <signal.h> -#include <log/logd.h> +#include <string.h> +#include <unistd.h> #include <sys/ptrace.h> #include <sys/wait.h> -#include <arpa/inet.h> -#include <assert.h> -#include "utility.h" +#include <backtrace/Backtrace.h> +#include <log/log.h> const int sleep_time_usec = 50000; // 0.05 seconds const int max_total_sleep_usec = 10000000; // 10 seconds @@ -34,10 +34,10 @@ const int max_total_sleep_usec = 10000000; // 10 seconds static int write_to_am(int fd, const char* buf, int len) { int to_write = len; while (to_write > 0) { - int written = TEMP_FAILURE_RETRY( write(fd, buf + len - to_write, to_write) ); + int written = TEMP_FAILURE_RETRY(write(fd, buf + len - to_write, to_write)); if (written < 0) { // hard failure - LOG("AM write failure (%d / %s)\n", errno, strerror(errno)); + ALOGE("AM write failure (%d / %s)\n", errno, strerror(errno)); return -1; } to_write -= written; @@ -45,35 +45,41 @@ static int write_to_am(int fd, const char* buf, int len) { return len; } -void _LOG(log_t* log, int scopeFlags, const char* fmt, ...) { - char buf[512]; - bool want_tfd_write; - bool want_log_write; - bool want_amfd_write; - int len = 0; +// Whitelist output desired in the logcat output. +bool is_allowed_in_logcat(enum logtype ltype) { + if ((ltype == ERROR) + || (ltype == HEADER) + || (ltype == REGISTERS) + || (ltype == BACKTRACE)) { + return true; + } + return false; +} +void _LOG(log_t* log, enum logtype ltype, const char* fmt, ...) { + bool write_to_tombstone = (log->tfd != -1); + bool write_to_logcat = is_allowed_in_logcat(ltype) + && (log->crashed_tid == log->current_tid); + bool write_to_activitymanager = (log->amfd != -1); + + char buf[512]; va_list ap; va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); - // where is the information going to go? - want_tfd_write = log && log->tfd >= 0; - want_log_write = IS_AT_FAULT(scopeFlags) && (!log || !log->quiet); - want_amfd_write = IS_AT_FAULT(scopeFlags) && !IS_SENSITIVE(scopeFlags) && log && log->amfd >= 0; - - // if we're going to need the literal string, generate it once here - if (want_tfd_write || want_amfd_write) { - vsnprintf(buf, sizeof(buf), fmt, ap); - len = strlen(buf); + size_t len = strlen(buf); + if (len <= 0) { + return; } - if (want_tfd_write) { - write(log->tfd, buf, len); + if (write_to_tombstone) { + TEMP_FAILURE_RETRY(write(log->tfd, buf, len)); } - if (want_log_write) { - // whatever goes to logcat also goes to the Activity Manager - __android_log_vprint(ANDROID_LOG_INFO, "DEBUG", fmt, ap); - if (want_amfd_write && len > 0) { + if (write_to_logcat) { + __android_log_buf_write(LOG_ID_CRASH, ANDROID_LOG_INFO, LOG_TAG, buf); + if (write_to_activitymanager) { int written = write_to_am(log->amfd, buf, len); if (written <= 0) { // timeout or other failure on write; stop informing the activity manager @@ -81,7 +87,6 @@ void _LOG(log_t* log, int scopeFlags, const char* fmt, ...) { } } } - va_end(ap); } int wait_for_signal(pid_t tid, int* total_sleep_time_usec) { @@ -91,25 +96,25 @@ int wait_for_signal(pid_t tid, int* total_sleep_time_usec) { if (n < 0) { if (errno == EAGAIN) continue; - LOG("waitpid failed: %s\n", strerror(errno)); + ALOGE("waitpid failed: %s\n", strerror(errno)); return -1; } else if (n > 0) { - XLOG("waitpid: n=%d status=%08x\n", n, status); + ALOGV("waitpid: n=%d status=%08x\n", n, status); if (WIFSTOPPED(status)) { return WSTOPSIG(status); } else { - LOG("unexpected waitpid response: n=%d, status=%08x\n", n, status); + ALOGE("unexpected waitpid response: n=%d, status=%08x\n", n, status); return -1; } } if (*total_sleep_time_usec > max_total_sleep_usec) { - LOG("timed out waiting for tid=%d to die\n", tid); + ALOGE("timed out waiting for tid=%d to die\n", tid); return -1; } // not ready yet - XLOG("not ready yet\n"); + ALOGV("not ready yet\n"); usleep(sleep_time_usec); *total_sleep_time_usec += sleep_time_usec; } @@ -119,7 +124,7 @@ void wait_for_stop(pid_t tid, int* total_sleep_time_usec) { siginfo_t si; while (TEMP_FAILURE_RETRY(ptrace(PTRACE_GETSIGINFO, tid, 0, &si)) < 0 && errno == ESRCH) { if (*total_sleep_time_usec > max_total_sleep_usec) { - LOG("timed out waiting for tid=%d to stop\n", tid); + ALOGE("timed out waiting for tid=%d to stop\n", tid); break; } @@ -127,3 +132,77 @@ void wait_for_stop(pid_t tid, int* total_sleep_time_usec) { *total_sleep_time_usec += sleep_time_usec; } } + +#if defined (__mips__) +#define DUMP_MEMORY_AS_ASCII 1 +#else +#define DUMP_MEMORY_AS_ASCII 0 +#endif + +void dump_memory(log_t* log, pid_t tid, uintptr_t addr) { + char code_buffer[64]; + char ascii_buffer[32]; + uintptr_t p, end; + + p = addr & ~(sizeof(long) - 1); + /* Dump 32 bytes before addr */ + p -= 32; + if (p > addr) { + /* catch underflow */ + p = 0; + } + /* Dump 256 bytes */ + end = p + 256; + /* catch overflow; 'end - p' has to be multiples of 16 */ + while (end < p) { + end -= 16; + } + + /* Dump the code around PC as: + * addr contents ascii + * 0000000000008d34 ef000000e8bd0090 e1b00000512fff1e ............../Q + * 0000000000008d44 ea00b1f9e92d0090 e3a070fcef000000 ......-..p...... + * On 32-bit machines, there are still 16 bytes per line but addresses and + * words are of course presented differently. + */ + while (p < end) { + char* asc_out = ascii_buffer; + + int len = snprintf(code_buffer, sizeof(code_buffer), "%" PRIPTR " ", p); + + for (size_t i = 0; i < 16/sizeof(long); i++) { + long data = ptrace(PTRACE_PEEKTEXT, tid, (void*)p, NULL); + if (data == -1 && errno != 0) { + // ptrace failed, probably because we're dumping memory in an + // unmapped or inaccessible page. +#ifdef __LP64__ + len += sprintf(code_buffer + len, "---------------- "); +#else + len += sprintf(code_buffer + len, "-------- "); +#endif + } else { + len += sprintf(code_buffer + len, "%" PRIPTR " ", + static_cast<uintptr_t>(data)); + } + +#if DUMP_MEMORY_AS_ASCII + for (size_t j = 0; j < sizeof(long); j++) { + /* + * Our isprint() allows high-ASCII characters that display + * differently (often badly) in different viewers, so we + * just use a simpler test. + */ + char val = (data >> (j*8)) & 0xff; + if (val >= 0x20 && val < 0x7f) { + *asc_out++ = val; + } else { + *asc_out++ = '.'; + } + } +#endif + p += sizeof(long); + } + *asc_out = '\0'; + _LOG(log, logtype::MEMORY, " %s %s\n", code_buffer, ascii_buffer); + } +} diff --git a/debuggerd/utility.h b/debuggerd/utility.h index 1f006ed..f2e2d29 100644 --- a/debuggerd/utility.h +++ b/debuggerd/utility.h @@ -18,49 +18,56 @@ #ifndef _DEBUGGERD_UTILITY_H #define _DEBUGGERD_UTILITY_H -#include <stddef.h> #include <stdbool.h> +#include <sys/types.h> + +// Figure out the abi based on defined macros. +#if defined(__arm__) +#define ABI_STRING "arm" +#elif defined(__aarch64__) +#define ABI_STRING "arm64" +#elif defined(__mips__) +#define ABI_STRING "mips" +#elif defined(__i386__) +#define ABI_STRING "x86" +#elif defined(__x86_64__) +#define ABI_STRING "x86_64" +#else +#error "Unsupported ABI" +#endif + typedef struct { /* tombstone file descriptor */ int tfd; /* Activity Manager socket file descriptor */ int amfd; - /* if true, does not log anything to the Android logcat or Activity Manager */ - bool quiet; + // The tid of the thread that crashed. + pid_t crashed_tid; + // The tid of the thread we are currently working with. + pid_t current_tid; } log_t; -/* Log information onto the tombstone. scopeFlags is a bitmask of the flags defined - * here. */ -void _LOG(log_t* log, int scopeFlags, const char *fmt, ...) - __attribute__ ((format(printf, 3, 4))); - -/* The message pertains specifically to the faulting thread / process */ -#define SCOPE_AT_FAULT (1 << 0) -/* The message contains sensitive information such as RAM contents */ -#define SCOPE_SENSITIVE (1 << 1) - -#define IS_AT_FAULT(x) (((x) & SCOPE_AT_FAULT) != 0) -#define IS_SENSITIVE(x) (((x) & SCOPE_SENSITIVE) != 0) - -/* Further helpful macros */ -#define LOG(fmt...) _LOG(NULL, SCOPE_AT_FAULT, fmt) +// List of types of logs to simplify the logging decision in _LOG +enum logtype { + ERROR, + HEADER, + THREAD, + REGISTERS, + BACKTRACE, + MAPS, + MEMORY, + STACK, + LOGS +}; -/* Set to 1 for normal debug traces */ -#if 0 -#define XLOG(fmt...) _LOG(NULL, SCOPE_AT_FAULT, fmt) -#else -#define XLOG(fmt...) do {} while(0) -#endif - -/* Set to 1 for chatty debug traces. Includes all resolved dynamic symbols */ -#if 0 -#define XLOG2(fmt...) _LOG(NULL, SCOPE_AT_FAULT, fmt) -#else -#define XLOG2(fmt...) do {} while(0) -#endif +/* Log information onto the tombstone. */ +void _LOG(log_t* log, logtype ltype, const char *fmt, ...) + __attribute__ ((format(printf, 3, 4))); int wait_for_signal(pid_t tid, int* total_sleep_time_usec); void wait_for_stop(pid_t tid, int* total_sleep_time_usec); +void dump_memory(log_t* log, pid_t tid, uintptr_t addr); + #endif // _DEBUGGERD_UTILITY_H diff --git a/debuggerd/vfp-crasher.c b/debuggerd/vfp-crasher.c deleted file mode 100644 index 7a19cdd..0000000 --- a/debuggerd/vfp-crasher.c +++ /dev/null @@ -1,7 +0,0 @@ -int main() -{ - extern void crash(void); - - crash(); - return 0; -} diff --git a/debuggerd/vfp.S b/debuggerd/vfp.S deleted file mode 100644 index 9744f6f..0000000 --- a/debuggerd/vfp.S +++ /dev/null @@ -1,43 +0,0 @@ - .text - .align 2 - .global crash - .type crash, %function -crash: - fconstd d0, #0 - fconstd d1, #1 - fconstd d2, #2 - fconstd d3, #3 - fconstd d4, #4 - fconstd d5, #5 - fconstd d6, #6 - fconstd d7, #7 - fconstd d8, #8 - fconstd d9, #9 - fconstd d10, #10 - fconstd d11, #11 - fconstd d12, #12 - fconstd d13, #13 - fconstd d14, #14 - fconstd d15, #15 -#ifdef WITH_VFP_D32 - fconstd d16, #16 - fconstd d17, #17 - fconstd d18, #18 - fconstd d19, #19 - fconstd d20, #20 - fconstd d21, #21 - fconstd d22, #22 - fconstd d23, #23 - fconstd d24, #24 - fconstd d25, #25 - fconstd d26, #26 - fconstd d27, #27 - fconstd d28, #28 - fconstd d29, #29 - fconstd d30, #30 - fconstd d31, #31 -#endif - mov r0, #0 - str r0, [r0] - bx lr - diff --git a/debuggerd/x86/machine.cpp b/debuggerd/x86/machine.cpp index 141f19a..57330c1 100644 --- a/debuggerd/x86/machine.cpp +++ b/debuggerd/x86/machine.cpp @@ -25,21 +25,21 @@ #include "../utility.h" #include "../machine.h" -void dump_memory_and_code(log_t* log, pid_t tid, int scope_flags) { +void dump_memory_and_code(log_t*, pid_t) { } -void dump_registers(log_t* log, pid_t tid, int scope_flags) { +void dump_registers(log_t* log, pid_t tid) { struct pt_regs r; if (ptrace(PTRACE_GETREGS, tid, 0, &r) == -1) { - _LOG(log, scope_flags, "cannot get registers: %s\n", strerror(errno)); + _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno)); return; } - _LOG(log, scope_flags, " eax %08lx ebx %08lx ecx %08lx edx %08lx\n", + _LOG(log, logtype::REGISTERS, " eax %08lx ebx %08lx ecx %08lx edx %08lx\n", r.eax, r.ebx, r.ecx, r.edx); - _LOG(log, scope_flags, " esi %08lx edi %08lx\n", + _LOG(log, logtype::REGISTERS, " esi %08lx edi %08lx\n", r.esi, r.edi); - _LOG(log, scope_flags, " xcs %08x xds %08x xes %08x xfs %08x xss %08x\n", + _LOG(log, logtype::REGISTERS, " xcs %08x xds %08x xes %08x xfs %08x xss %08x\n", r.xcs, r.xds, r.xes, r.xfs, r.xss); - _LOG(log, scope_flags, " eip %08lx ebp %08lx esp %08lx flags %08lx\n", + _LOG(log, logtype::REGISTERS, " eip %08lx ebp %08lx esp %08lx flags %08lx\n", r.eip, r.ebp, r.esp, r.eflags); } diff --git a/debuggerd/x86_64/crashglue.S b/debuggerd/x86_64/crashglue.S new file mode 100644 index 0000000..4d2a5c0 --- /dev/null +++ b/debuggerd/x86_64/crashglue.S @@ -0,0 +1,15 @@ +.globl crash1 +.globl crashnostack + +crash1: + movl $0xa5a50000, %eax + movl $0xa5a50001, %ebx + movl $0xa5a50002, %ecx + + movl $0, %edx + jmp *%rdx + + +crashnostack: + movl $0, %ebp + jmp *%rbp diff --git a/debuggerd/x86_64/machine.cpp b/debuggerd/x86_64/machine.cpp new file mode 100755 index 0000000..af4f35a --- /dev/null +++ b/debuggerd/x86_64/machine.cpp @@ -0,0 +1,51 @@ +/* +** Copyright 2013, 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 <stddef.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/ptrace.h> +#include <sys/user.h> + +#include "../utility.h" +#include "../machine.h" + +void dump_memory_and_code(log_t*, pid_t) { +} + +void dump_registers(log_t* log, pid_t tid) { + struct user_regs_struct r; + if (ptrace(PTRACE_GETREGS, tid, 0, &r) == -1) { + _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno)); + return; + } + _LOG(log, logtype::REGISTERS, " rax %016lx rbx %016lx rcx %016lx rdx %016lx\n", + r.rax, r.rbx, r.rcx, r.rdx); + _LOG(log, logtype::REGISTERS, " rsi %016lx rdi %016lx\n", + r.rsi, r.rdi); + _LOG(log, logtype::REGISTERS, " r8 %016lx r9 %016lx r10 %016lx r11 %016lx\n", + r.r8, r.r9, r.r10, r.r11); + _LOG(log, logtype::REGISTERS, " r12 %016lx r13 %016lx r14 %016lx r15 %016lx\n", + r.r12, r.r13, r.r14, r.r15); + _LOG(log, logtype::REGISTERS, " cs %016lx ss %016lx\n", + r.cs, r.ss); + _LOG(log, logtype::REGISTERS, " rip %016lx rbp %016lx rsp %016lx eflags %016lx\n", + r.rip, r.rbp, r.rsp, r.eflags); +} diff --git a/fastboot/Android.mk b/fastboot/Android.mk index 05ddf2a..73794a0 100644 --- a/fastboot/Android.mk +++ b/fastboot/Android.mk @@ -21,7 +21,7 @@ LOCAL_C_INCLUDES := $(LOCAL_PATH)/../mkbootimg \ LOCAL_SRC_FILES := protocol.c engine.c bootimg.c fastboot.c util.c fs.c LOCAL_MODULE := fastboot LOCAL_MODULE_TAGS := debug -LOCAL_CFLAGS += -std=gnu99 +LOCAL_CFLAGS += -std=gnu99 -Werror ifeq ($(HOST_OS),linux) LOCAL_SRC_FILES += usb_linux.c util_linux.c @@ -72,6 +72,7 @@ ifeq ($(HOST_OS),linux) include $(CLEAR_VARS) LOCAL_SRC_FILES := usbtest.c usb_linux.c util.c LOCAL_MODULE := usbtest +LOCAL_CFLAGS := -Werror include $(BUILD_HOST_EXECUTABLE) endif diff --git a/fastboot/engine.c b/fastboot/engine.c index 0fab703..2f90e41 100644 --- a/fastboot/engine.c +++ b/fastboot/engine.c @@ -30,10 +30,10 @@ #include "fs.h" #include <errno.h> -#include <stdio.h> -#include <stdlib.h> #include <stdarg.h> #include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> #include <string.h> #include <sys/stat.h> #include <sys/types.h> @@ -45,6 +45,10 @@ #include <sys/mman.h> #endif +#ifndef __unused +#define __unused __attribute__((__unused__)) +#endif + #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) #define OP_DOWNLOAD 1 @@ -102,18 +106,19 @@ int fb_getvar(struct usb_handle *usb, char *response, const char *fmt, ...) * Not all devices report the filesystem type, so don't report any errors, * just return false. */ -int fb_format_supported(usb_handle *usb, const char *partition) +int fb_format_supported(usb_handle *usb, const char *partition, const char *type_override) { - char response[FB_RESPONSE_SZ + 1] = {0,}; + char fs_type[FB_RESPONSE_SZ + 1] = {0,}; int status; - unsigned int i; - status = fb_getvar(usb, response, "partition-type:%s", partition); + if (type_override) { + return !!fs_get_generator(type_override); + } + status = fb_getvar(usb, fs_type, "partition-type:%s", partition); if (status) { return 0; } - - return !!fs_get_generator(response); + return !!fs_get_generator(fs_type); } static int cb_default(Action *a, int status, char *resp) @@ -195,9 +200,7 @@ void fb_queue_flash_sparse(const char *ptn, struct sparse_file *s, unsigned sz) static int match(char *str, const char **value, unsigned count) { - const char *val; unsigned n; - int len; for (n = 0; n < count; n++) { const char *val = value[n]; @@ -323,7 +326,7 @@ void fb_queue_query_save(const char *var, char *dest, unsigned dest_size) a->func = cb_save; } -static int cb_do_nothing(Action *a, int status, char *resp) +static int cb_do_nothing(Action *a __unused, int status __unused, char *resp __unused) { fprintf(stderr,"\n"); return 0; diff --git a/fastboot/fastboot.c b/fastboot/fastboot.c index 4fd1a8e..266d0b5 100644 --- a/fastboot/fastboot.c +++ b/fastboot/fastboot.c @@ -28,21 +28,21 @@ #define _LARGEFILE64_SOURCE -#include <stdio.h> -#include <stdlib.h> -#include <stdbool.h> -#include <stdint.h> -#include <string.h> +#include <ctype.h> #include <errno.h> #include <fcntl.h> -#include <unistd.h> -#include <limits.h> -#include <ctype.h> #include <getopt.h> - +#include <inttypes.h> +#include <limits.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> #include <sys/time.h> #include <sys/types.h> -#include <sys/stat.h> +#include <unistd.h> #include <bootimg.h> #include <sparse/sparse.h> @@ -71,7 +71,6 @@ static usb_handle *usb = 0; static const char *serial = 0; static const char *product = 0; static const char *cmdline = 0; -static int wipe_data = 0; static unsigned short vendor_id = 0; static int long_listing = 0; static int64_t sparse_limit = -1; @@ -100,10 +99,11 @@ static struct { char sig_name[13]; char part_name[9]; bool is_optional; -} images[3] = { +} images[4] = { {"boot.img", "boot.sig", "boot", false}, {"recovery.img", "recovery.sig", "recovery", true}, {"system.img", "system.sig", "system", false}, + {"tos.img", "tos.sig", "tos", true}, }; void get_my_path(char *path); @@ -120,6 +120,8 @@ char *find_item(const char *item, const char *product) fn = "recovery.img"; } else if(!strcmp(item,"system")) { fn = "system.img"; + } else if(!strcmp(item,"tos")) { + fn = "tos.img"; } else if(!strcmp(item,"userdata")) { fn = "userdata.img"; } else if(!strcmp(item,"cache")) { @@ -266,7 +268,7 @@ usb_handle *open_device(void) announce = 0; fprintf(stderr,"< waiting for device >\n"); } - sleep(1); + usleep(1000); } } @@ -285,10 +287,13 @@ void usage(void) "\n" "commands:\n" " update <filename> reflash device from update.zip\n" - " flashall flash boot + recovery + system\n" + " flashall flash boot, system, and if found,\n" + " recovery, tos\n" " flash <partition> [ <filename> ] write a file to a flash partition\n" " erase <partition> erase a flash partition\n" - " format <partition> format a flash partition \n" + " format[:[<fs type>][:[<size>]] <partition> format a flash partition.\n" + " Can override the fs type and/or\n" + " size the bootloader reports.\n" " getvar <variable> display a bootloader variable\n" " boot <kernel> [ <ramdisk> ] download and boot kernel\n" " flash:raw boot <kernel> [ <ramdisk> ] create bootimage and flash it\n" @@ -309,10 +314,12 @@ void usage(void) " -p <product> specify product name\n" " -c <cmdline> override kernel commandline\n" " -i <vendor id> specify a custom USB vendor id\n" - " -b <base_addr> specify a custom kernel base address. default: 0x10000000\n" - " -n <page size> specify the nand page size. default: 2048\n" - " -S <size>[K|M|G] automatically sparse files greater than\n" - " size. 0 to disable\n" + " -b <base_addr> specify a custom kernel base address.\n" + " default: 0x10000000\n" + " -n <page size> specify the nand page size.\n" + " default: 2048\n" + " -S <size>[K|M|G] automatically sparse files greater\n" + " than size. 0 to disable\n" ); } @@ -419,7 +426,7 @@ static int unzip_to_file(zipfile_t zip, char *name) return -1; } - if (write(fd, data, sz) != sz) { + if (write(fd, data, sz) != (ssize_t)sz) { fd = -1; } @@ -570,7 +577,7 @@ static int64_t get_target_sparse_limit(struct usb_handle *usb) if (!status) { limit = strtoul(response, NULL, 0); if (limit > 0) { - fprintf(stderr, "target reported max download size of %lld bytes\n", + fprintf(stderr, "target reported max download size of %" PRId64 " bytes\n", limit); } } @@ -613,7 +620,7 @@ static int needs_erase(const char *part) /* The function fb_format_supported() currently returns the value * we want, so just call it. */ - return fb_format_supported(usb, part); + return fb_format_supported(usb, part, NULL); } static int load_buf_fd(usb_handle *usb, int fd, @@ -657,7 +664,7 @@ static int load_buf(usb_handle *usb, const char *fname, fd = open(fname, O_RDONLY | O_BINARY); if (fd < 0) { - die("cannot open '%s'\n", fname); + return -1; } return load_buf_fd(usb, fd, buf); @@ -713,7 +720,7 @@ void do_update(usb_handle *usb, char *fn, int erase_first) int fd; int rc; struct fastboot_buffer buf; - int i; + size_t i; queue_info_dump(); @@ -787,7 +794,7 @@ void do_flashall(usb_handle *usb, int erase_first) void *data; unsigned sz; struct fastboot_buffer buf; - int i; + size_t i; queue_info_dump(); @@ -819,7 +826,6 @@ void do_flashall(usb_handle *usb, int erase_first) int do_oem_command(int argc, char **argv) { - int i; char command[256]; if (argc <= 1) return 0; @@ -876,9 +882,12 @@ static int64_t parse_num(const char *arg) return num; } -void fb_perform_format(const char *partition, int skip_if_not_supported) +void fb_perform_format(const char *partition, int skip_if_not_supported, + const char *type_override, const char *size_override) { - char pType[FB_RESPONSE_SZ + 1], pSize[FB_RESPONSE_SZ + 1]; + char pTypeBuff[FB_RESPONSE_SZ + 1], pSizeBuff[FB_RESPONSE_SZ + 1]; + char *pType = pTypeBuff; + char *pSize = pSizeBuff; unsigned int limit = INT_MAX; struct fastboot_buffer buf; const char *errMsg = NULL; @@ -897,12 +906,28 @@ void fb_perform_format(const char *partition, int skip_if_not_supported) errMsg = "Can't determine partition type.\n"; goto failed; } + if (type_override) { + if (strcmp(type_override, pType)) { + fprintf(stderr, + "Warning: %s type is %s, but %s was requested for formating.\n", + partition, pType, type_override); + } + pType = (char *)type_override; + } status = fb_getvar(usb, pSize, "partition-size:%s", partition); if (status) { errMsg = "Unable to get partition size\n"; goto failed; } + if (size_override) { + if (strcmp(size_override, pSize)) { + fprintf(stderr, + "Warning: %s size is %s, but %s was requested for formating.\n", + partition, pSize, size_override); + } + pSize = (char *)size_override; + } gen = fs_get_generator(pType); if (!gen) { @@ -953,13 +978,13 @@ int main(int argc, char **argv) unsigned sz; int status; int c; - int r; const struct option longopts[] = { {"base", required_argument, 0, 'b'}, {"kernel_offset", required_argument, 0, 'k'}, {"page_size", required_argument, 0, 'n'}, {"ramdisk_offset", required_argument, 0, 'r'}, + {"tags_offset", required_argument, 0, 't'}, {"help", 0, 0, 'h'}, {0, 0, 0, 0} }; @@ -967,8 +992,7 @@ int main(int argc, char **argv) serial = getenv("ANDROID_SERIAL"); while (1) { - int option_index = 0; - c = getopt_long(argc, argv, "wub:k:n:r:s:S:lp:c:i:m:h", longopts, NULL); + c = getopt_long(argc, argv, "wub:k:n:r:t:s:S:lp:c:i:m:h", longopts, NULL); if (c < 0) { break; } @@ -1009,6 +1033,9 @@ int main(int argc, char **argv) case 'r': ramdisk_offset = strtoul(optarg, 0, 16); break; + case 't': + tags_offset = strtoul(optarg, 0, 16); + break; case 's': serial = optarg; break; @@ -1060,18 +1087,42 @@ int main(int argc, char **argv) } else if(!strcmp(*argv, "erase")) { require(2); - if (fb_format_supported(usb, argv[1])) { + if (fb_format_supported(usb, argv[1], NULL)) { fprintf(stderr, "******** Did you mean to fastboot format this partition?\n"); } fb_queue_erase(argv[1]); skip(2); - } else if(!strcmp(*argv, "format")) { + } else if(!strncmp(*argv, "format", strlen("format"))) { + char *overrides; + char *type_override = NULL; + char *size_override = NULL; require(2); + /* + * Parsing for: "format[:[type][:[size]]]" + * Some valid things: + * - select ontly the size, and leave default fs type: + * format::0x4000000 userdata + * - default fs type and size: + * format userdata + * format:: userdata + */ + overrides = strchr(*argv, ':'); + if (overrides) { + overrides++; + size_override = strchr(overrides, ':'); + if (size_override) { + size_override[0] = '\0'; + size_override++; + } + type_override = overrides; + } + if (type_override && !type_override[0]) type_override = NULL; + if (size_override && !size_override[0]) size_override = NULL; if (erase_first && needs_erase(argv[1])) { fb_queue_erase(argv[1]); } - fb_perform_format(argv[1], 0); + fb_perform_format(argv[1], 0, type_override, size_override); skip(2); } else if(!strcmp(*argv, "signature")) { require(2); @@ -1159,12 +1210,13 @@ int main(int argc, char **argv) if (wants_wipe) { fb_queue_erase("userdata"); - fb_perform_format("userdata", 1); + fb_perform_format("userdata", 1, NULL, NULL); fb_queue_erase("cache"); - fb_perform_format("cache", 1); + fb_perform_format("cache", 1, NULL, NULL); } if (wants_reboot) { fb_queue_reboot(); + fb_queue_wait_for_disconnect(); } else if (wants_reboot_bootloader) { fb_queue_command("reboot-bootloader", "rebooting into bootloader"); fb_queue_wait_for_disconnect(); diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h index c510a36..fc5d4f4 100644 --- a/fastboot/fastboot.h +++ b/fastboot/fastboot.h @@ -45,7 +45,7 @@ char *fb_get_error(void); /* engine.c - high level command queue engine */ int fb_getvar(struct usb_handle *usb, char *response, const char *fmt, ...); -int fb_format_supported(usb_handle *usb, const char *partition); +int fb_format_supported(usb_handle *usb, const char *partition, const char *type_override); void fb_queue_flash(const char *ptn, void *data, unsigned sz); void fb_queue_flash_sparse(const char *ptn, struct sparse_file *s, unsigned sz); void fb_queue_erase(const char *ptn); diff --git a/fastboot/fs.c b/fastboot/fs.c index 6a1f9e6..cd4b880 100644 --- a/fastboot/fs.c +++ b/fastboot/fs.c @@ -39,12 +39,12 @@ static const struct fs_generator { }; -const struct fs_generator* fs_get_generator(const char* name) +const struct fs_generator* fs_get_generator(const char *fs_type) { unsigned i; for (i = 0; i < sizeof(generators) / sizeof(*generators); i++) - if (!strcmp(generators[i].fs_type, name)) + if (!strcmp(generators[i].fs_type, fs_type)) return generators + i; return NULL; diff --git a/fastboot/fs.h b/fastboot/fs.h index 65b9555..8444081 100644 --- a/fastboot/fs.h +++ b/fastboot/fs.h @@ -1,11 +1,11 @@ #ifndef _FS_H_ -#define _FH_H_ +#define _FS_H_ #include <stdint.h> struct fs_generator; -const struct fs_generator* fs_get_generator(const char* name); +const struct fs_generator* fs_get_generator(const char *fs_type); int fs_generator_generate(const struct fs_generator* gen, int tmpFileNo, long long partSize); #endif diff --git a/fastboot/protocol.c b/fastboot/protocol.c index a0e0fd4..84e9837 100644 --- a/fastboot/protocol.c +++ b/fastboot/protocol.c @@ -110,7 +110,6 @@ static int _command_start(usb_handle *usb, const char *cmd, unsigned size, char *response) { int cmdsize = strlen(cmd); - int r; if(response) { response[0] = 0; @@ -189,8 +188,6 @@ static int _command_send(usb_handle *usb, const char *cmd, static int _command_send_no_data(usb_handle *usb, const char *cmd, char *response) { - int r; - return _command_start(usb, cmd, 0, response); } diff --git a/fastboot/usb_linux.c b/fastboot/usb_linux.c index f2ce226..a45f9f8 100644 --- a/fastboot/usb_linux.c +++ b/fastboot/usb_linux.c @@ -125,9 +125,6 @@ static int filter_usb_device(char* sysfs_name, unsigned i; unsigned e; - struct stat st; - int result; - if(check(ptr, len, USB_DT_DEVICE, USB_DT_DEVICE_SIZE)) return -1; dev = (void*) ptr; diff --git a/fastboot/usb_windows.c b/fastboot/usb_windows.c index 07f7be2..0d13863 100644 --- a/fastboot/usb_windows.c +++ b/fastboot/usb_windows.c @@ -152,7 +152,7 @@ usb_handle* do_usb_open(const wchar_t* interface_name) { } int usb_write(usb_handle* handle, const void* data, int len) { - unsigned long time_out = 500 + len * 8; + unsigned long time_out = 5000; unsigned long written = 0; unsigned count = 0; int ret; @@ -178,7 +178,7 @@ int usb_write(usb_handle* handle, const void* data, int len) { count += written; len -= written; - data += written; + data = (const char *)data + written; if (len == 0) return count; @@ -194,7 +194,7 @@ int usb_write(usb_handle* handle, const void* data, int len) { } int usb_read(usb_handle *handle, void* data, int len) { - unsigned long time_out = 500 + len * 8; + unsigned long time_out = 0; unsigned long read = 0; int ret; @@ -212,7 +212,7 @@ int usb_read(usb_handle *handle, void* data, int len) { DBG("usb_read got: %ld, expected: %d, errno: %d\n", read, xfer, errno); if (ret) { return read; - } else if (errno != ERROR_SEM_TIMEOUT) { + } else { // assume ERROR_INVALID_HANDLE indicates we are disconnected if (errno == ERROR_INVALID_HANDLE) usb_kick(handle); diff --git a/fastboot/usbtest.c b/fastboot/usbtest.c index b8fb9e2..e6e2b37 100644 --- a/fastboot/usbtest.c +++ b/fastboot/usbtest.c @@ -88,14 +88,14 @@ int match_loop(usb_ifc_info *info) int test_null(usb_handle *usb) { - int i; + unsigned i; unsigned char buf[4096]; memset(buf, 0xee, 4096); long long t0, t1; t0 = NOW(); for(i = 0; i < arg_count; i++) { - if(usb_write(usb, buf, arg_size) != arg_size) { + if(usb_write(usb, buf, arg_size) != (int)arg_size) { fprintf(stderr,"write failed (%s)\n", strerror(errno)); return -1; } @@ -107,13 +107,13 @@ int test_null(usb_handle *usb) int test_zero(usb_handle *usb) { - int i; + unsigned i; unsigned char buf[4096]; long long t0, t1; t0 = NOW(); for(i = 0; i < arg_count; i++) { - if(usb_read(usb, buf, arg_size) != arg_size) { + if(usb_read(usb, buf, arg_size) != (int)arg_size) { fprintf(stderr,"read failed (%s)\n", strerror(errno)); return -1; } @@ -130,11 +130,11 @@ struct int (*test)(usb_handle *usb); const char *help; } tests[] = { - { "list", printifc, 0, "list interfaces" }, + { "list", printifc, NULL, "list interfaces" }, { "send", match_null, test_null, "send to null interface" }, { "recv", match_zero, test_zero, "recv from zero interface" }, - { "loop", match_loop, 0, "exercise loopback interface" }, - {}, + { "loop", match_loop, NULL, "exercise loopback interface" }, + { NULL, NULL, NULL, NULL }, }; int usage(void) diff --git a/fastbootd/Android.mk b/fastbootd/Android.mk index 0f32dbf..6aa7400 100644 --- a/fastbootd/Android.mk +++ b/fastbootd/Android.mk @@ -45,19 +45,17 @@ LOCAL_MODULE_TAGS := optional LOCAL_CFLAGS := -Wall -Werror -Wno-unused-parameter -DFLASH_CERT LOCAL_LDFLAGS := -ldl -LOCAL_SHARED_LIBRARIES := \ - libhardware \ - libcrypto \ - libhardware_legacy \ - libmdnssd - LOCAL_STATIC_LIBRARIES := \ - libsparse_static \ libc \ + libcrypto_static \ libcutils \ + libmdnssd \ + libsparse_static \ libz -#LOCAL_FORCE_STATIC_EXECUTABLE := true +LOCAL_HAL_STATIC_LIBRARIES := libvendortrigger + +LOCAL_FORCE_STATIC_EXECUTABLE := true include $(BUILD_EXECUTABLE) @@ -84,21 +82,11 @@ LOCAL_FORCE_STATIC_EXECUTABLE := true include $(BUILD_EXECUTABLE) +# vendor trigger HAL include $(CLEAR_VARS) - -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/include \ - -LOCAL_STATIC_LIBRARIES := \ - $(EXTRA_STATIC_LIBS) \ - libcutils - -LOCAL_SRC_FILES := \ - other/vendor_trigger.c - +LOCAL_CFLAGS := -Wall -Werror LOCAL_MODULE := libvendortrigger.default LOCAL_MODULE_TAGS := optional -LOCAL_CFLAGS := -Wall -Werror -Wno-unused-parameter - - -include $(BUILD_SHARED_LIBRARY) +LOCAL_SRC_FILES := vendor_trigger_default.c +LOCAL_STATIC_LIBRARIES := libcutils +include $(BUILD_STATIC_LIBRARY) diff --git a/fastbootd/commands.c b/fastbootd/commands.c index 063e1a6..98b7866 100644 --- a/fastbootd/commands.c +++ b/fastbootd/commands.c @@ -124,9 +124,9 @@ static void cmd_boot(struct protocol_handle *phandle, const char *arg) goto error; } - kernel_ptr = (void *)((unsigned) ptr + hdr->page_size); - ramdisk_ptr = (void *)((unsigned) kernel_ptr + kernel_actual); - second_ptr = (void *)((unsigned) ramdisk_ptr + ramdisk_actual); + kernel_ptr = (void *)((uintptr_t) ptr + hdr->page_size); + ramdisk_ptr = (void *)((uintptr_t) kernel_ptr + kernel_actual); + second_ptr = (void *)((uintptr_t) ramdisk_ptr + ramdisk_actual); D(INFO, "preparing to boot"); // Prepares boot physical address. Addresses from header are ignored diff --git a/fastbootd/commands/boot.c b/fastbootd/commands/boot.c index 8da9a28..922914b 100644 --- a/fastbootd/commands/boot.c +++ b/fastbootd/commands/boot.c @@ -89,10 +89,10 @@ long kexec_load(unsigned int entry, unsigned long nr_segments, * Kernel address is not set into kernel_phys * Ramdisk is set to position relative to kernel */ -int prepare_boot_linux(unsigned kernel_phys, void *kernel_addr, int kernel_size, - unsigned ramdisk_phys, void *ramdisk_addr, int ramdisk_size, - unsigned second_phys, void *second_addr, int second_size, - unsigned atags_phys, void *atags_addr, int atags_size) { +int prepare_boot_linux(uintptr_t kernel_phys, void *kernel_addr, int kernel_size, + uintptr_t ramdisk_phys, void *ramdisk_addr, int ramdisk_size, + uintptr_t second_phys, void *second_addr, int second_size, + uintptr_t atags_phys, void *atags_addr, int atags_size) { struct kexec_segment segment[4]; int segment_count = 2; unsigned entry = START_ADDRESS + KEXEC_ARM_ZIMAGE_OFFSET; diff --git a/fastbootd/commands/boot.h b/fastbootd/commands/boot.h index 901c38a..a5efd01 100644 --- a/fastbootd/commands/boot.h +++ b/fastbootd/commands/boot.h @@ -40,8 +40,8 @@ #define KEXEC_TYPE_DEFAULT 0 #define KEXEC_TYPE_CRASH 1 -int prepare_boot_linux(unsigned, void *, int, unsigned, void *, int, - unsigned, void *, int, unsigned, void *, int); +int prepare_boot_linux(uintptr_t, void *, int, uintptr_t, void *, int, + uintptr_t, void *, int, uintptr_t, void *, int); unsigned *create_atags(unsigned *, int, const struct boot_img_hdr *, int *); long kexec_load(unsigned int, unsigned long, struct kexec_segment *, unsigned long); char *read_atags(const char *, int *); diff --git a/fastbootd/protocol.c b/fastbootd/protocol.c index 0086b4a..3908020 100644 --- a/fastbootd/protocol.c +++ b/fastbootd/protocol.c @@ -142,7 +142,7 @@ void fastboot_data(struct protocol_handle *phandle, size_t len) char response[64]; ssize_t ret; - snprintf(response, 64, "DATA%08x", len); + snprintf(response, 64, "DATA%08zx", len); ret = protocol_handle_write(phandle, response, strlen(response)); if (ret < 0) return; diff --git a/fastbootd/transport.c b/fastbootd/transport.c index ce8f9d0..9a16fd7 100644 --- a/fastbootd/transport.c +++ b/fastbootd/transport.c @@ -55,7 +55,7 @@ int transport_handle_download(struct transport_handle *thandle, size_t len) ftruncate(fd, len); buffer = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - if (buffer == NULL) { + if (buffer == MAP_FAILED) { D(ERR, "mmap(%zu) failed: %d %s", len, errno, strerror(errno)); goto err; } diff --git a/fastbootd/trigger.c b/fastbootd/trigger.c index e63e64d..df0f895 100644 --- a/fastbootd/trigger.c +++ b/fastbootd/trigger.c @@ -39,52 +39,19 @@ static const int version = 1; -static struct vendor_trigger_t *triggers = NULL; - int load_trigger() { - int err; - hw_module_t* module; - hw_device_t* device; int libversion; - err = hw_get_module(TRIGGER_MODULE_ID, (hw_module_t const**)&module); - - if (err == 0) { - err = module->methods->open(module, NULL, &device); - - if (err == 0) { - triggers = (struct vendor_trigger_t *) device; - } else { - D(WARN, "Libvendor load error"); - return 1; - } - } - else { - D(WARN, "Libvendor not load: %s", strerror(-err)); - return 0; + if (trigger_init() != 0) { + D(ERR, "libvendortrigger failed to initialize"); + return 1; } - if (triggers->check_version != NULL && - triggers->check_version(version, &libversion)) { - - triggers = NULL; + if (trigger_check_version(version, &libversion)) { D(ERR, "Library report incompability"); return 1; } - D(INFO, "libvendortrigger loaded"); + D(INFO, "libvendortrigger loaded"); return 0; } - -int trigger_oem_cmd(const char *arg, const char **response) { - if (triggers != NULL && triggers->oem_cmd != NULL) - return triggers->oem_cmd(arg, response); - return 0; -} - -int trigger_gpt_layout(struct GPT_content *table) { - if (triggers != NULL && triggers->gpt_layout != NULL) - return triggers->gpt_layout(table); - return 0; -} - diff --git a/fastbootd/trigger.h b/fastbootd/trigger.h index 404acb4..d2d9573 100644 --- a/fastbootd/trigger.h +++ b/fastbootd/trigger.h @@ -37,9 +37,4 @@ int load_trigger(); -/* same as in struct triggers */ - -int trigger_gpt_layout(struct GPT_content *table); -int trigger_oem_cmd(const char *arg, const char **response); - #endif diff --git a/fastbootd/include/vendor_trigger.h b/fastbootd/vendor_trigger.h index 51204fa..0c83be6 100644 --- a/fastbootd/include/vendor_trigger.h +++ b/fastbootd/vendor_trigger.h @@ -32,38 +32,37 @@ #ifndef __VENDOR_TRIGGER_H_ #define __VENDOR_TRIGGER_H_ -#define TRIGGER_MODULE_ID "fastbootd" -#include <hardware/hardware.h> - __BEGIN_DECLS struct GPT_entry_raw; struct GPT_content; /* - * Structer with function pointers may become longer in the future + * Implemented in libvendortrigger to handle platform-specific behavior. */ -struct vendor_trigger_t { - struct hw_device_t common; - - /* - * This function runs at the beggining and shoud never be changed - * - * version is number parameter indicating version on the fastbootd side - * libversion is version indicateing version of the library version - * - * returns 0 if it can cooperate with the current version and 1 in opposite - */ - int (*check_version)(const int version, int *libversion); +/* + * trigger_init() is called once at startup time before calling any other method + * + * returns 0 on success and nonzero on error + */ +int trigger_init(void); +/* + * This function runs once after trigger_init completes. + * + * version is number parameter indicating version on the fastbootd side + * libversion is version indicateing version of the library version + * + * returns 0 if it can cooperate with the current version and 1 in opposite + */ +int trigger_check_version(const int version, int *libversion); - /* - * Return value -1 forbid the action from the vendor site and sets errno - */ - int (* gpt_layout)(struct GPT_content *); - int (* oem_cmd)(const char *arg, const char **response); -}; +/* + * Return value -1 forbid the action from the vendor site and sets errno + */ +int trigger_gpt_layout(struct GPT_content *); +int trigger_oem_cmd(const char *arg, const char **response); __END_DECLS diff --git a/fastbootd/other/vendor_trigger.c b/fastbootd/vendor_trigger_default.c index 101959b..3627024 100644 --- a/fastbootd/other/vendor_trigger.c +++ b/fastbootd/vendor_trigger_default.c @@ -30,67 +30,29 @@ */ #include <stdlib.h> - -#include "vendor_trigger.h" -#include "debug.h" - -unsigned int debug_level = DEBUG; +#include <cutils/klog.h> +#include <vendor_trigger.h> static const int version = 1; -int check_version(const int fastboot_version, int *libversion) { - *libversion = version; - return !(fastboot_version == version); -} - -int gpt_layout(struct GPT_content *table) { - D(DEBUG, "message from libvendor"); +int trigger_init(void) { + klog_init(); + klog_set_level(7); return 0; } -int oem_cmd(const char *arg, const char **response) { - D(DEBUG, "message from libvendor, oem catched request %s", arg); - return 0; +int trigger_check_version(const int fastboot_version, int *libversion) { + KLOG_DEBUG("fastbootd", "%s: %d (%d)", __func__, fastboot_version, version); + *libversion = version; + return !(fastboot_version == version); } -static int close_triggers(struct vendor_trigger_t *dev) -{ - if (dev) - free(dev); - +int trigger_gpt_layout(struct GPT_content *table) { + KLOG_DEBUG("fastbootd", "%s: %p", __func__, table); return 0; } -static int open_triggers(const struct hw_module_t *module, char const *name, - struct hw_device_t **device) { - struct vendor_trigger_t *dev = malloc(sizeof(struct vendor_trigger_t)); - klog_init(); - klog_set_level(6); - - memset(dev, 0, sizeof(*dev)); - dev->common.module = (struct hw_module_t *) module; - dev->common.close = (int (*)(struct hw_device_t *)) close_triggers; - - dev->gpt_layout = gpt_layout; - dev->oem_cmd = oem_cmd; - - *device = (struct hw_device_t *) dev; - +int trigger_oem_cmd(const char *arg, const char **response) { + KLOG_DEBUG("fastbootd", "%s: %s", __func__, arg); return 0; } - - -static struct hw_module_methods_t trigger_module_methods = { - .open = open_triggers, -}; - -struct hw_module_t HAL_MODULE_INFO_SYM = { - .tag = HARDWARE_MODULE_TAG, - .version_major = 1, - .version_minor = 0, - .id = TRIGGER_MODULE_ID, - .name = "vendor trigger library for fastbootd", - .author = "Google, Inc.", - .methods = &trigger_module_methods, -}; - diff --git a/fs_mgr/Android.mk b/fs_mgr/Android.mk index 790598a..7cffc37 100644 --- a/fs_mgr/Android.mk +++ b/fs_mgr/Android.mk @@ -3,7 +3,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) -LOCAL_SRC_FILES:= fs_mgr.c fs_mgr_verity.c +LOCAL_SRC_FILES:= fs_mgr.c fs_mgr_verity.c fs_mgr_fstab.c LOCAL_C_INCLUDES := $(LOCAL_PATH)/include @@ -11,6 +11,7 @@ LOCAL_MODULE:= libfs_mgr LOCAL_STATIC_LIBRARIES := liblogwrap libmincrypt libext4_utils_static LOCAL_C_INCLUDES += system/extras/ext4_utils LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include +LOCAL_CFLAGS := -Werror include $(BUILD_STATIC_LIBRARY) @@ -31,5 +32,7 @@ LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED) LOCAL_STATIC_LIBRARIES := libfs_mgr liblogwrap libcutils liblog libc libmincrypt libext4_utils_static +LOCAL_CFLAGS := -Werror + include $(BUILD_EXECUTABLE) diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c index 24ce806..dcda005 100644 --- a/fs_mgr/fs_mgr.c +++ b/fs_mgr/fs_mgr.c @@ -54,55 +54,6 @@ #define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a))) -struct flag_list { - const char *name; - unsigned flag; -}; - -static struct flag_list mount_flags[] = { - { "noatime", MS_NOATIME }, - { "noexec", MS_NOEXEC }, - { "nosuid", MS_NOSUID }, - { "nodev", MS_NODEV }, - { "nodiratime", MS_NODIRATIME }, - { "ro", MS_RDONLY }, - { "rw", 0 }, - { "remount", MS_REMOUNT }, - { "bind", MS_BIND }, - { "rec", MS_REC }, - { "unbindable", MS_UNBINDABLE }, - { "private", MS_PRIVATE }, - { "slave", MS_SLAVE }, - { "shared", MS_SHARED }, - { "defaults", 0 }, - { 0, 0 }, -}; - -static struct flag_list fs_mgr_flags[] = { - { "wait", MF_WAIT }, - { "check", MF_CHECK }, - { "encryptable=",MF_CRYPT }, - { "nonremovable",MF_NONREMOVABLE }, - { "voldmanaged=",MF_VOLDMANAGED}, - { "length=", MF_LENGTH }, - { "recoveryonly",MF_RECOVERYONLY }, - { "swapprio=", MF_SWAPPRIO }, - { "zramsize=", MF_ZRAMSIZE }, - { "verify", MF_VERIFY }, - { "noemulatedsd", MF_NOEMULATEDSD }, - { "defaults", 0 }, - { 0, 0 }, -}; - -struct fs_mgr_flag_values { - char *key_loc; - long long part_length; - char *label; - int partnum; - int swap_prio; - unsigned int zram_size; -}; - /* * gettime() - returns the time in seconds of the system's monotonic clock or * zero on error. @@ -133,269 +84,6 @@ static int wait_for_file(const char *filename, int timeout) return ret; } -static int parse_flags(char *flags, struct flag_list *fl, - struct fs_mgr_flag_values *flag_vals, - char *fs_options, int fs_options_len) -{ - int f = 0; - int i; - char *p; - char *savep; - - /* initialize flag values. If we find a relevant flag, we'll - * update the value */ - if (flag_vals) { - memset(flag_vals, 0, sizeof(*flag_vals)); - flag_vals->partnum = -1; - flag_vals->swap_prio = -1; /* negative means it wasn't specified. */ - } - - /* initialize fs_options to the null string */ - if (fs_options && (fs_options_len > 0)) { - fs_options[0] = '\0'; - } - - p = strtok_r(flags, ",", &savep); - while (p) { - /* Look for the flag "p" in the flag list "fl" - * If not found, the loop exits with fl[i].name being null. - */ - for (i = 0; fl[i].name; i++) { - if (!strncmp(p, fl[i].name, strlen(fl[i].name))) { - f |= fl[i].flag; - if ((fl[i].flag == MF_CRYPT) && flag_vals) { - /* The encryptable flag is followed by an = and the - * location of the keys. Get it and return it. - */ - flag_vals->key_loc = strdup(strchr(p, '=') + 1); - } else if ((fl[i].flag == MF_LENGTH) && flag_vals) { - /* The length flag is followed by an = and the - * size of the partition. Get it and return it. - */ - flag_vals->part_length = strtoll(strchr(p, '=') + 1, NULL, 0); - } else if ((fl[i].flag == MF_VOLDMANAGED) && flag_vals) { - /* The voldmanaged flag is followed by an = and the - * label, a colon and the partition number or the - * word "auto", e.g. - * voldmanaged=sdcard:3 - * Get and return them. - */ - char *label_start; - char *label_end; - char *part_start; - - label_start = strchr(p, '=') + 1; - label_end = strchr(p, ':'); - if (label_end) { - flag_vals->label = strndup(label_start, - (int) (label_end - label_start)); - part_start = strchr(p, ':') + 1; - if (!strcmp(part_start, "auto")) { - flag_vals->partnum = -1; - } else { - flag_vals->partnum = strtol(part_start, NULL, 0); - } - } else { - ERROR("Warning: voldmanaged= flag malformed\n"); - } - } else if ((fl[i].flag == MF_SWAPPRIO) && flag_vals) { - flag_vals->swap_prio = strtoll(strchr(p, '=') + 1, NULL, 0); - } else if ((fl[i].flag == MF_ZRAMSIZE) && flag_vals) { - flag_vals->zram_size = strtoll(strchr(p, '=') + 1, NULL, 0); - } - break; - } - } - - if (!fl[i].name) { - if (fs_options) { - /* It's not a known flag, so it must be a filesystem specific - * option. Add it to fs_options if it was passed in. - */ - strlcat(fs_options, p, fs_options_len); - strlcat(fs_options, ",", fs_options_len); - } else { - /* fs_options was not passed in, so if the flag is unknown - * it's an error. - */ - ERROR("Warning: unknown flag %s\n", p); - } - } - p = strtok_r(NULL, ",", &savep); - } - -out: - if (fs_options && fs_options[0]) { - /* remove the last trailing comma from the list of options */ - fs_options[strlen(fs_options) - 1] = '\0'; - } - - return f; -} - -struct fstab *fs_mgr_read_fstab(const char *fstab_path) -{ - FILE *fstab_file; - int cnt, entries; - ssize_t len; - size_t alloc_len = 0; - char *line = NULL; - const char *delim = " \t"; - char *save_ptr, *p; - struct fstab *fstab = NULL; - struct fstab_rec *recs; - struct fs_mgr_flag_values flag_vals; -#define FS_OPTIONS_LEN 1024 - char tmp_fs_options[FS_OPTIONS_LEN]; - - fstab_file = fopen(fstab_path, "r"); - if (!fstab_file) { - ERROR("Cannot open file %s\n", fstab_path); - return 0; - } - - entries = 0; - while ((len = getline(&line, &alloc_len, fstab_file)) != -1) { - /* if the last character is a newline, shorten the string by 1 byte */ - if (line[len - 1] == '\n') { - line[len - 1] = '\0'; - } - /* Skip any leading whitespace */ - p = line; - while (isspace(*p)) { - p++; - } - /* ignore comments or empty lines */ - if (*p == '#' || *p == '\0') - continue; - entries++; - } - - if (!entries) { - ERROR("No entries found in fstab\n"); - goto err; - } - - /* Allocate and init the fstab structure */ - fstab = calloc(1, sizeof(struct fstab)); - fstab->num_entries = entries; - fstab->fstab_filename = strdup(fstab_path); - fstab->recs = calloc(fstab->num_entries, sizeof(struct fstab_rec)); - - fseek(fstab_file, 0, SEEK_SET); - - cnt = 0; - while ((len = getline(&line, &alloc_len, fstab_file)) != -1) { - /* if the last character is a newline, shorten the string by 1 byte */ - if (line[len - 1] == '\n') { - line[len - 1] = '\0'; - } - - /* Skip any leading whitespace */ - p = line; - while (isspace(*p)) { - p++; - } - /* ignore comments or empty lines */ - if (*p == '#' || *p == '\0') - continue; - - /* If a non-comment entry is greater than the size we allocated, give an - * error and quit. This can happen in the unlikely case the file changes - * between the two reads. - */ - if (cnt >= entries) { - ERROR("Tried to process more entries than counted\n"); - break; - } - - if (!(p = strtok_r(line, delim, &save_ptr))) { - ERROR("Error parsing mount source\n"); - goto err; - } - fstab->recs[cnt].blk_device = strdup(p); - - if (!(p = strtok_r(NULL, delim, &save_ptr))) { - ERROR("Error parsing mount_point\n"); - goto err; - } - fstab->recs[cnt].mount_point = strdup(p); - - if (!(p = strtok_r(NULL, delim, &save_ptr))) { - ERROR("Error parsing fs_type\n"); - goto err; - } - fstab->recs[cnt].fs_type = strdup(p); - - if (!(p = strtok_r(NULL, delim, &save_ptr))) { - ERROR("Error parsing mount_flags\n"); - goto err; - } - tmp_fs_options[0] = '\0'; - fstab->recs[cnt].flags = parse_flags(p, mount_flags, NULL, - tmp_fs_options, FS_OPTIONS_LEN); - - /* fs_options are optional */ - if (tmp_fs_options[0]) { - fstab->recs[cnt].fs_options = strdup(tmp_fs_options); - } else { - fstab->recs[cnt].fs_options = NULL; - } - - if (!(p = strtok_r(NULL, delim, &save_ptr))) { - ERROR("Error parsing fs_mgr_options\n"); - goto err; - } - fstab->recs[cnt].fs_mgr_flags = parse_flags(p, fs_mgr_flags, - &flag_vals, NULL, 0); - fstab->recs[cnt].key_loc = flag_vals.key_loc; - fstab->recs[cnt].length = flag_vals.part_length; - fstab->recs[cnt].label = flag_vals.label; - fstab->recs[cnt].partnum = flag_vals.partnum; - fstab->recs[cnt].swap_prio = flag_vals.swap_prio; - fstab->recs[cnt].zram_size = flag_vals.zram_size; - cnt++; - } - fclose(fstab_file); - free(line); - return fstab; - -err: - fclose(fstab_file); - free(line); - if (fstab) - fs_mgr_free_fstab(fstab); - return NULL; -} - -void fs_mgr_free_fstab(struct fstab *fstab) -{ - int i; - - if (!fstab) { - return; - } - - for (i = 0; i < fstab->num_entries; i++) { - /* Free the pointers return by strdup(3) */ - free(fstab->recs[i].blk_device); - free(fstab->recs[i].mount_point); - free(fstab->recs[i].fs_type); - free(fstab->recs[i].fs_options); - free(fstab->recs[i].key_loc); - free(fstab->recs[i].label); - } - - /* Free the fstab_recs array created by calloc(3) */ - free(fstab->recs); - - /* Free the fstab filename */ - free(fstab->fstab_filename); - - /* Free fstab */ - free(fstab); -} - static void check_fs(char *blk_device, char *fs_type, char *target) { int status; @@ -428,15 +116,24 @@ static void check_fs(char *blk_device, char *fs_type, char *target) umount(target); } - INFO("Running %s on %s\n", E2FSCK_BIN, blk_device); + /* + * Some system images do not have e2fsck for licensing reasons + * (e.g. recent SDK system images). Detect these and skip the check. + */ + if (access(E2FSCK_BIN, X_OK)) { + INFO("Not running %s on %s (executable not in system image)\n", + E2FSCK_BIN, blk_device); + } else { + INFO("Running %s on %s\n", E2FSCK_BIN, blk_device); - ret = android_fork_execvp_ext(ARRAY_SIZE(e2fsck_argv), e2fsck_argv, - &status, true, LOG_KLOG | LOG_FILE, - true, FSCK_LOG_FILE); + ret = android_fork_execvp_ext(ARRAY_SIZE(e2fsck_argv), e2fsck_argv, + &status, true, LOG_KLOG | LOG_FILE, + true, FSCK_LOG_FILE); - if (ret < 0) { - /* No need to check for error in fork, we can't really handle it now */ - ERROR("Failed trying to run %s\n", E2FSCK_BIN); + if (ret < 0) { + /* No need to check for error in fork, we can't really handle it now */ + ERROR("Failed trying to run %s\n", E2FSCK_BIN); + } } } @@ -823,71 +520,3 @@ int fs_mgr_get_crypt_info(struct fstab *fstab, char *key_loc, char *real_blk_dev return 0; } - -/* Add an entry to the fstab, and return 0 on success or -1 on error */ -int fs_mgr_add_entry(struct fstab *fstab, - const char *mount_point, const char *fs_type, - const char *blk_device, long long length) -{ - struct fstab_rec *new_fstab_recs; - int n = fstab->num_entries; - - new_fstab_recs = (struct fstab_rec *) - realloc(fstab->recs, sizeof(struct fstab_rec) * (n + 1)); - - if (!new_fstab_recs) { - return -1; - } - - /* A new entry was added, so initialize it */ - memset(&new_fstab_recs[n], 0, sizeof(struct fstab_rec)); - new_fstab_recs[n].mount_point = strdup(mount_point); - new_fstab_recs[n].fs_type = strdup(fs_type); - new_fstab_recs[n].blk_device = strdup(blk_device); - new_fstab_recs[n].length = 0; - - /* Update the fstab struct */ - fstab->recs = new_fstab_recs; - fstab->num_entries++; - - return 0; -} - -struct fstab_rec *fs_mgr_get_entry_for_mount_point(struct fstab *fstab, const char *path) -{ - int i; - - if (!fstab) { - return NULL; - } - - for (i = 0; i < fstab->num_entries; i++) { - int len = strlen(fstab->recs[i].mount_point); - if (strncmp(path, fstab->recs[i].mount_point, len) == 0 && - (path[len] == '\0' || path[len] == '/')) { - return &fstab->recs[i]; - } - } - - return NULL; -} - -int fs_mgr_is_voldmanaged(struct fstab_rec *fstab) -{ - return fstab->fs_mgr_flags & MF_VOLDMANAGED; -} - -int fs_mgr_is_nonremovable(struct fstab_rec *fstab) -{ - return fstab->fs_mgr_flags & MF_NONREMOVABLE; -} - -int fs_mgr_is_encryptable(struct fstab_rec *fstab) -{ - return fstab->fs_mgr_flags & MF_CRYPT; -} - -int fs_mgr_is_noemulatedsd(struct fstab_rec *fstab) -{ - return fstab->fs_mgr_flags & MF_NOEMULATEDSD; -} diff --git a/fs_mgr/fs_mgr_fstab.c b/fs_mgr/fs_mgr_fstab.c new file mode 100644 index 0000000..f86fc6a --- /dev/null +++ b/fs_mgr/fs_mgr_fstab.c @@ -0,0 +1,401 @@ +/* + * Copyright (C) 2014 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 <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/mount.h> + +#include "fs_mgr_priv.h" + +struct fs_mgr_flag_values { + char *key_loc; + long long part_length; + char *label; + int partnum; + int swap_prio; + unsigned int zram_size; +}; + +struct flag_list { + const char *name; + unsigned flag; +}; + +static struct flag_list mount_flags[] = { + { "noatime", MS_NOATIME }, + { "noexec", MS_NOEXEC }, + { "nosuid", MS_NOSUID }, + { "nodev", MS_NODEV }, + { "nodiratime", MS_NODIRATIME }, + { "ro", MS_RDONLY }, + { "rw", 0 }, + { "remount", MS_REMOUNT }, + { "bind", MS_BIND }, + { "rec", MS_REC }, + { "unbindable", MS_UNBINDABLE }, + { "private", MS_PRIVATE }, + { "slave", MS_SLAVE }, + { "shared", MS_SHARED }, + { "defaults", 0 }, + { 0, 0 }, +}; + +static struct flag_list fs_mgr_flags[] = { + { "wait", MF_WAIT }, + { "check", MF_CHECK }, + { "encryptable=",MF_CRYPT }, + { "nonremovable",MF_NONREMOVABLE }, + { "voldmanaged=",MF_VOLDMANAGED}, + { "length=", MF_LENGTH }, + { "recoveryonly",MF_RECOVERYONLY }, + { "swapprio=", MF_SWAPPRIO }, + { "zramsize=", MF_ZRAMSIZE }, + { "verify", MF_VERIFY }, + { "noemulatedsd", MF_NOEMULATEDSD }, + { "defaults", 0 }, + { 0, 0 }, +}; + +static int parse_flags(char *flags, struct flag_list *fl, + struct fs_mgr_flag_values *flag_vals, + char *fs_options, int fs_options_len) +{ + int f = 0; + int i; + char *p; + char *savep; + + /* initialize flag values. If we find a relevant flag, we'll + * update the value */ + if (flag_vals) { + memset(flag_vals, 0, sizeof(*flag_vals)); + flag_vals->partnum = -1; + flag_vals->swap_prio = -1; /* negative means it wasn't specified. */ + } + + /* initialize fs_options to the null string */ + if (fs_options && (fs_options_len > 0)) { + fs_options[0] = '\0'; + } + + p = strtok_r(flags, ",", &savep); + while (p) { + /* Look for the flag "p" in the flag list "fl" + * If not found, the loop exits with fl[i].name being null. + */ + for (i = 0; fl[i].name; i++) { + if (!strncmp(p, fl[i].name, strlen(fl[i].name))) { + f |= fl[i].flag; + if ((fl[i].flag == MF_CRYPT) && flag_vals) { + /* The encryptable flag is followed by an = and the + * location of the keys. Get it and return it. + */ + flag_vals->key_loc = strdup(strchr(p, '=') + 1); + } else if ((fl[i].flag == MF_LENGTH) && flag_vals) { + /* The length flag is followed by an = and the + * size of the partition. Get it and return it. + */ + flag_vals->part_length = strtoll(strchr(p, '=') + 1, NULL, 0); + } else if ((fl[i].flag == MF_VOLDMANAGED) && flag_vals) { + /* The voldmanaged flag is followed by an = and the + * label, a colon and the partition number or the + * word "auto", e.g. + * voldmanaged=sdcard:3 + * Get and return them. + */ + char *label_start; + char *label_end; + char *part_start; + + label_start = strchr(p, '=') + 1; + label_end = strchr(p, ':'); + if (label_end) { + flag_vals->label = strndup(label_start, + (int) (label_end - label_start)); + part_start = strchr(p, ':') + 1; + if (!strcmp(part_start, "auto")) { + flag_vals->partnum = -1; + } else { + flag_vals->partnum = strtol(part_start, NULL, 0); + } + } else { + ERROR("Warning: voldmanaged= flag malformed\n"); + } + } else if ((fl[i].flag == MF_SWAPPRIO) && flag_vals) { + flag_vals->swap_prio = strtoll(strchr(p, '=') + 1, NULL, 0); + } else if ((fl[i].flag == MF_ZRAMSIZE) && flag_vals) { + flag_vals->zram_size = strtoll(strchr(p, '=') + 1, NULL, 0); + } + break; + } + } + + if (!fl[i].name) { + if (fs_options) { + /* It's not a known flag, so it must be a filesystem specific + * option. Add it to fs_options if it was passed in. + */ + strlcat(fs_options, p, fs_options_len); + strlcat(fs_options, ",", fs_options_len); + } else { + /* fs_options was not passed in, so if the flag is unknown + * it's an error. + */ + ERROR("Warning: unknown flag %s\n", p); + } + } + p = strtok_r(NULL, ",", &savep); + } + + if (fs_options && fs_options[0]) { + /* remove the last trailing comma from the list of options */ + fs_options[strlen(fs_options) - 1] = '\0'; + } + + return f; +} + +struct fstab *fs_mgr_read_fstab(const char *fstab_path) +{ + FILE *fstab_file; + int cnt, entries; + ssize_t len; + size_t alloc_len = 0; + char *line = NULL; + const char *delim = " \t"; + char *save_ptr, *p; + struct fstab *fstab = NULL; + struct fs_mgr_flag_values flag_vals; +#define FS_OPTIONS_LEN 1024 + char tmp_fs_options[FS_OPTIONS_LEN]; + + fstab_file = fopen(fstab_path, "r"); + if (!fstab_file) { + ERROR("Cannot open file %s\n", fstab_path); + return 0; + } + + entries = 0; + while ((len = getline(&line, &alloc_len, fstab_file)) != -1) { + /* if the last character is a newline, shorten the string by 1 byte */ + if (line[len - 1] == '\n') { + line[len - 1] = '\0'; + } + /* Skip any leading whitespace */ + p = line; + while (isspace(*p)) { + p++; + } + /* ignore comments or empty lines */ + if (*p == '#' || *p == '\0') + continue; + entries++; + } + + if (!entries) { + ERROR("No entries found in fstab\n"); + goto err; + } + + /* Allocate and init the fstab structure */ + fstab = calloc(1, sizeof(struct fstab)); + fstab->num_entries = entries; + fstab->fstab_filename = strdup(fstab_path); + fstab->recs = calloc(fstab->num_entries, sizeof(struct fstab_rec)); + + fseek(fstab_file, 0, SEEK_SET); + + cnt = 0; + while ((len = getline(&line, &alloc_len, fstab_file)) != -1) { + /* if the last character is a newline, shorten the string by 1 byte */ + if (line[len - 1] == '\n') { + line[len - 1] = '\0'; + } + + /* Skip any leading whitespace */ + p = line; + while (isspace(*p)) { + p++; + } + /* ignore comments or empty lines */ + if (*p == '#' || *p == '\0') + continue; + + /* If a non-comment entry is greater than the size we allocated, give an + * error and quit. This can happen in the unlikely case the file changes + * between the two reads. + */ + if (cnt >= entries) { + ERROR("Tried to process more entries than counted\n"); + break; + } + + if (!(p = strtok_r(line, delim, &save_ptr))) { + ERROR("Error parsing mount source\n"); + goto err; + } + fstab->recs[cnt].blk_device = strdup(p); + + if (!(p = strtok_r(NULL, delim, &save_ptr))) { + ERROR("Error parsing mount_point\n"); + goto err; + } + fstab->recs[cnt].mount_point = strdup(p); + + if (!(p = strtok_r(NULL, delim, &save_ptr))) { + ERROR("Error parsing fs_type\n"); + goto err; + } + fstab->recs[cnt].fs_type = strdup(p); + + if (!(p = strtok_r(NULL, delim, &save_ptr))) { + ERROR("Error parsing mount_flags\n"); + goto err; + } + tmp_fs_options[0] = '\0'; + fstab->recs[cnt].flags = parse_flags(p, mount_flags, NULL, + tmp_fs_options, FS_OPTIONS_LEN); + + /* fs_options are optional */ + if (tmp_fs_options[0]) { + fstab->recs[cnt].fs_options = strdup(tmp_fs_options); + } else { + fstab->recs[cnt].fs_options = NULL; + } + + if (!(p = strtok_r(NULL, delim, &save_ptr))) { + ERROR("Error parsing fs_mgr_options\n"); + goto err; + } + fstab->recs[cnt].fs_mgr_flags = parse_flags(p, fs_mgr_flags, + &flag_vals, NULL, 0); + fstab->recs[cnt].key_loc = flag_vals.key_loc; + fstab->recs[cnt].length = flag_vals.part_length; + fstab->recs[cnt].label = flag_vals.label; + fstab->recs[cnt].partnum = flag_vals.partnum; + fstab->recs[cnt].swap_prio = flag_vals.swap_prio; + fstab->recs[cnt].zram_size = flag_vals.zram_size; + cnt++; + } + fclose(fstab_file); + free(line); + return fstab; + +err: + fclose(fstab_file); + free(line); + if (fstab) + fs_mgr_free_fstab(fstab); + return NULL; +} + +void fs_mgr_free_fstab(struct fstab *fstab) +{ + int i; + + if (!fstab) { + return; + } + + for (i = 0; i < fstab->num_entries; i++) { + /* Free the pointers return by strdup(3) */ + free(fstab->recs[i].blk_device); + free(fstab->recs[i].mount_point); + free(fstab->recs[i].fs_type); + free(fstab->recs[i].fs_options); + free(fstab->recs[i].key_loc); + free(fstab->recs[i].label); + } + + /* Free the fstab_recs array created by calloc(3) */ + free(fstab->recs); + + /* Free the fstab filename */ + free(fstab->fstab_filename); + + /* Free fstab */ + free(fstab); +} + +/* Add an entry to the fstab, and return 0 on success or -1 on error */ +int fs_mgr_add_entry(struct fstab *fstab, + const char *mount_point, const char *fs_type, + const char *blk_device) +{ + struct fstab_rec *new_fstab_recs; + int n = fstab->num_entries; + + new_fstab_recs = (struct fstab_rec *) + realloc(fstab->recs, sizeof(struct fstab_rec) * (n + 1)); + + if (!new_fstab_recs) { + return -1; + } + + /* A new entry was added, so initialize it */ + memset(&new_fstab_recs[n], 0, sizeof(struct fstab_rec)); + new_fstab_recs[n].mount_point = strdup(mount_point); + new_fstab_recs[n].fs_type = strdup(fs_type); + new_fstab_recs[n].blk_device = strdup(blk_device); + new_fstab_recs[n].length = 0; + + /* Update the fstab struct */ + fstab->recs = new_fstab_recs; + fstab->num_entries++; + + return 0; +} + +struct fstab_rec *fs_mgr_get_entry_for_mount_point(struct fstab *fstab, const char *path) +{ + int i; + + if (!fstab) { + return NULL; + } + + for (i = 0; i < fstab->num_entries; i++) { + int len = strlen(fstab->recs[i].mount_point); + if (strncmp(path, fstab->recs[i].mount_point, len) == 0 && + (path[len] == '\0' || path[len] == '/')) { + return &fstab->recs[i]; + } + } + + return NULL; +} + +int fs_mgr_is_voldmanaged(struct fstab_rec *fstab) +{ + return fstab->fs_mgr_flags & MF_VOLDMANAGED; +} + +int fs_mgr_is_nonremovable(struct fstab_rec *fstab) +{ + return fstab->fs_mgr_flags & MF_NONREMOVABLE; +} + +int fs_mgr_is_encryptable(struct fstab_rec *fstab) +{ + return fstab->fs_mgr_flags & MF_CRYPT; +} + +int fs_mgr_is_noemulatedsd(struct fstab_rec *fstab) +{ + return fstab->fs_mgr_flags & MF_NOEMULATEDSD; +} diff --git a/fs_mgr/fs_mgr_main.c b/fs_mgr/fs_mgr_main.c index 4bde4a1..e5a00d5 100644 --- a/fs_mgr/fs_mgr_main.c +++ b/fs_mgr/fs_mgr_main.c @@ -80,10 +80,10 @@ int main(int argc, char *argv[]) int a_flag=0; int u_flag=0; int n_flag=0; - char *n_name; - char *n_blk_dev; - char *fstab_file; - struct fstab *fstab; + char *n_name=NULL; + char *n_blk_dev=NULL; + char *fstab_file=NULL; + struct fstab *fstab=NULL; klog_init(); klog_set_level(6); diff --git a/fs_mgr/fs_mgr_verity.c b/fs_mgr/fs_mgr_verity.c index 1549316..c9a2a9b 100644 --- a/fs_mgr/fs_mgr_verity.c +++ b/fs_mgr/fs_mgr_verity.c @@ -36,8 +36,7 @@ #include "mincrypt/sha.h" #include "mincrypt/sha256.h" -#include "ext4_utils.h" -#include "ext4.h" +#include "ext4_sb.h" #include "fs_mgr_priv.h" #include "fs_mgr_priv_verity.h" @@ -86,7 +85,6 @@ static RSAPublicKey *load_key(char *path) static int verify_table(char *signature, char *table, int table_length) { - int fd; RSAPublicKey *key; uint8_t hash_buf[SHA_DIGEST_SIZE]; int retval = -1; @@ -122,6 +120,7 @@ static int get_target_device_size(char *blk_device, uint64_t *device_size) { int data_device; struct ext4_super_block sb; + struct fs_info info = {0}; data_device = open(blk_device, O_RDONLY); if (data_device < 0) { @@ -141,7 +140,7 @@ static int get_target_device_size(char *blk_device, uint64_t *device_size) return -1; } - ext4_parse_sb(&sb); + ext4_parse_sb(&sb, &info); *device_size = info.len; close(data_device); diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h index 0f90c32..835cf64 100644 --- a/fs_mgr/include/fs_mgr.h +++ b/fs_mgr/include/fs_mgr.h @@ -57,7 +57,7 @@ int fs_mgr_get_crypt_info(struct fstab *fstab, char *key_loc, char *real_blk_device, int size); int fs_mgr_add_entry(struct fstab *fstab, const char *mount_point, const char *fs_type, - const char *blk_device, long long length); + const char *blk_device); struct fstab_rec *fs_mgr_get_entry_for_mount_point(struct fstab *fstab, const char *path); int fs_mgr_is_voldmanaged(struct fstab_rec *fstab); int fs_mgr_is_nonremovable(struct fstab_rec *fstab); diff --git a/gpttool/Android.mk b/gpttool/Android.mk index b8f9844..64ad945 100644 --- a/gpttool/Android.mk +++ b/gpttool/Android.mk @@ -5,6 +5,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := gpttool.c LOCAL_STATIC_LIBRARIES := libz +LOCAL_CFLAGS := -Werror LOCAL_MODULE := gpttool diff --git a/gpttool/gpttool.c b/gpttool/gpttool.c index d3f08fe..398362f 100644 --- a/gpttool/gpttool.c +++ b/gpttool/gpttool.c @@ -1,5 +1,4 @@ -/* system/core/gpttool/gpttool.c -** +/* ** Copyright 2011, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,18 +14,18 @@ ** limitations under the License. */ +#include <fcntl.h> #include <stdio.h> #include <stdlib.h> -#include <unistd.h> #include <string.h> -#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <unistd.h> #include <zlib.h> #include <linux/fs.h> -#include <sys/stat.h> - typedef unsigned char u8; typedef unsigned short u16; typedef unsigned int u32; @@ -252,11 +251,9 @@ int parse_ptn(struct ptable *ptbl, char *x) int main(int argc, char **argv) { struct ptable ptbl; - struct efi_entry *entry; struct efi_header *hdr = &ptbl.header; - struct stat s; u32 n; - u64 sz, blk; + u64 sz; int fd; const char *device; int real_disk = 0; diff --git a/healthd/healthd.cpp b/healthd/healthd.cpp index 9b84c3e..d30e771 100644 --- a/healthd/healthd.cpp +++ b/healthd/healthd.cpp @@ -126,7 +126,7 @@ static void uevent_init(void) { KLOG_ERROR(LOG_TAG, "uevent_init: uevent_open_socket failed\n"); } -#define UEVENT_MSG_LEN 1024 +#define UEVENT_MSG_LEN 2048 static void uevent_event(void) { char msg[UEVENT_MSG_LEN+2]; char *cp; diff --git a/healthd/healthd_board_default.cpp b/healthd/healthd_board_default.cpp index b2bb516..ed4ddb4 100644 --- a/healthd/healthd_board_default.cpp +++ b/healthd/healthd_board_default.cpp @@ -16,13 +16,13 @@ #include <healthd.h> -void healthd_board_init(struct healthd_config *config) +void healthd_board_init(struct healthd_config*) { // use defaults } -int healthd_board_battery_update(struct android::BatteryProperties *props) +int healthd_board_battery_update(struct android::BatteryProperties*) { // return 0 to log periodic polled battery status to kernel log return 0; diff --git a/include/android/log.h b/include/android/log.h index 0ea4c29..1c171b7 100644 --- a/include/android/log.h +++ b/include/android/log.h @@ -98,8 +98,16 @@ int __android_log_write(int prio, const char *tag, const char *text); */ int __android_log_print(int prio, const char *tag, const char *fmt, ...) #if defined(__GNUC__) +#ifdef __USE_MINGW_ANSI_STDIO +#if __USE_MINGW_ANSI_STDIO + __attribute__ ((format(gnu_printf, 3, 4))) +#else __attribute__ ((format(printf, 3, 4))) #endif +#else + __attribute__ ((format(printf, 3, 4))) +#endif +#endif ; /* @@ -110,15 +118,23 @@ int __android_log_vprint(int prio, const char *tag, const char *fmt, va_list ap); /* - * Log an assertion failure and SIGTRAP the process to have a chance - * to inspect it, if a debugger is attached. This uses the FATAL priority. + * Log an assertion failure and abort the process to have a chance + * to inspect it if a debugger is attached. This uses the FATAL priority. */ void __android_log_assert(const char *cond, const char *tag, - const char *fmt, ...) + const char *fmt, ...) #if defined(__GNUC__) __attribute__ ((noreturn)) +#ifdef __USE_MINGW_ANSI_STDIO +#if __USE_MINGW_ANSI_STDIO + __attribute__ ((format(gnu_printf, 3, 4))) +#else __attribute__ ((format(printf, 3, 4))) #endif +#else + __attribute__ ((format(printf, 3, 4))) +#endif +#endif ; #ifdef __cplusplus diff --git a/include/backtrace/Backtrace.h b/include/backtrace/Backtrace.h index bd4134c..e07d322 100644 --- a/include/backtrace/Backtrace.h +++ b/include/backtrace/Backtrace.h @@ -17,6 +17,7 @@ #ifndef _BACKTRACE_BACKTRACE_H #define _BACKTRACE_BACKTRACE_H +#include <inttypes.h> #include <stdint.h> #include <string> @@ -25,6 +26,14 @@ #include <backtrace/backtrace_constants.h> #include <backtrace/BacktraceMap.h> +#if __LP64__ +#define PRIPTR "016" PRIxPTR +typedef uint64_t word_t; +#else +#define PRIPTR "08" PRIxPTR +typedef uint32_t word_t; +#endif + struct backtrace_frame_data_t { size_t num; // The current fame number. uintptr_t pc; // The absolute pc. @@ -38,6 +47,14 @@ struct backtrace_frame_data_t { // Forward declarations. class BacktraceImpl; +#if defined(__APPLE__) +struct __darwin_ucontext; +typedef __darwin_ucontext ucontext_t; +#else +struct ucontext; +typedef ucontext ucontext_t; +#endif + class Backtrace { public: // Create the correct Backtrace object based on what is to be unwound. @@ -55,7 +72,7 @@ public: virtual ~Backtrace(); // Get the current stack trace and store in the backtrace_ structure. - virtual bool Unwind(size_t num_ignore_frames); + virtual bool Unwind(size_t num_ignore_frames, ucontext_t* context = NULL); // Get the function name and offset into the function given the pc. // If the string is empty, then no valid function name was found. @@ -65,7 +82,7 @@ public: virtual const backtrace_map_t* FindMap(uintptr_t pc); // Read the data at a specific address. - virtual bool ReadWord(uintptr_t ptr, uint32_t* out_value) = 0; + virtual bool ReadWord(uintptr_t ptr, word_t* out_value) = 0; // Create a string representing the formatted line of backtrace information // for a single frame. @@ -96,7 +113,7 @@ public: protected: Backtrace(BacktraceImpl* impl, pid_t pid, BacktraceMap* map); - virtual bool VerifyReadWordArgs(uintptr_t ptr, uint32_t* out_value); + virtual bool VerifyReadWordArgs(uintptr_t ptr, word_t* out_value); bool BuildMap(); diff --git a/include/backtrace/BacktraceMap.h b/include/backtrace/BacktraceMap.h index 06da2f4..c717f09 100644 --- a/include/backtrace/BacktraceMap.h +++ b/include/backtrace/BacktraceMap.h @@ -18,6 +18,7 @@ #define _BACKTRACE_BACKTRACE_MAP_H #include <stdint.h> +#include <sys/types.h> #ifdef USE_MINGW // MINGW does not define these constants. #define PROT_NONE 0 @@ -45,7 +46,7 @@ public: virtual ~BacktraceMap(); // Get the map data structure for the given address. - const backtrace_map_t* Find(uintptr_t addr); + virtual const backtrace_map_t* Find(uintptr_t addr); // The flags returned are the same flags as used by the mmap call. // The values are PROT_*. diff --git a/include/corkscrew/backtrace.h b/include/corkscrew/backtrace.h deleted file mode 100644 index 556ad04..0000000 --- a/include/corkscrew/backtrace.h +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -/* A stack unwinder. */ - -#ifndef _CORKSCREW_BACKTRACE_H -#define _CORKSCREW_BACKTRACE_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include <sys/types.h> -#include <corkscrew/ptrace.h> -#include <corkscrew/map_info.h> -#include <corkscrew/symbol_table.h> - -/* - * Describes a single frame of a backtrace. - */ -typedef struct { - uintptr_t absolute_pc; /* absolute PC offset */ - uintptr_t stack_top; /* top of stack for this frame */ - size_t stack_size; /* size of this stack frame */ -} backtrace_frame_t; - -/* - * Describes the symbols associated with a backtrace frame. - */ -typedef struct { - uintptr_t relative_pc; /* relative frame PC offset from the start of the library, - or the absolute PC if the library is unknown */ - uintptr_t relative_symbol_addr; /* relative offset of the symbol from the start of the - library or 0 if the library is unknown */ - char* map_name; /* executable or library name, or NULL if unknown */ - char* symbol_name; /* symbol name, or NULL if unknown */ - char* demangled_name; /* demangled symbol name, or NULL if unknown */ -} backtrace_symbol_t; - -/* - * Unwinds the call stack for the current thread of execution. - * Populates the backtrace array with the program counters from the call stack. - * Returns the number of frames collected, or -1 if an error occurred. - */ -ssize_t unwind_backtrace(backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth); - -/* - * Unwinds the call stack for a thread within this process. - * Populates the backtrace array with the program counters from the call stack. - * Returns the number of frames collected, or -1 if an error occurred. - * - * The task is briefly suspended while the backtrace is being collected. - */ -ssize_t unwind_backtrace_thread(pid_t tid, backtrace_frame_t* backtrace, - size_t ignore_depth, size_t max_depth); - -/* - * Unwinds the call stack of a task within a remote process using ptrace(). - * Populates the backtrace array with the program counters from the call stack. - * Returns the number of frames collected, or -1 if an error occurred. - */ -ssize_t unwind_backtrace_ptrace(pid_t tid, const ptrace_context_t* context, - backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth); - -/* - * Gets the symbols for each frame of a backtrace. - * The symbols array must be big enough to hold one symbol record per frame. - * The symbols must later be freed using free_backtrace_symbols. - */ -void get_backtrace_symbols(const backtrace_frame_t* backtrace, size_t frames, - backtrace_symbol_t* backtrace_symbols); - -/* - * Gets the symbols for each frame of a backtrace from a remote process. - * The symbols array must be big enough to hold one symbol record per frame. - * The symbols must later be freed using free_backtrace_symbols. - */ -void get_backtrace_symbols_ptrace(const ptrace_context_t* context, - const backtrace_frame_t* backtrace, size_t frames, - backtrace_symbol_t* backtrace_symbols); - -/* - * Frees the storage associated with backtrace symbols. - */ -void free_backtrace_symbols(backtrace_symbol_t* backtrace_symbols, size_t frames); - -enum { - // A hint for how big to make the line buffer for format_backtrace_line - MAX_BACKTRACE_LINE_LENGTH = 800, -}; - -/** - * Formats a line from a backtrace as a zero-terminated string into the specified buffer. - */ -void format_backtrace_line(unsigned frameNumber, const backtrace_frame_t* frame, - const backtrace_symbol_t* symbol, char* buffer, size_t bufferSize); - -#ifdef __cplusplus -} -#endif - -#endif // _CORKSCREW_BACKTRACE_H diff --git a/include/corkscrew/map_info.h b/include/corkscrew/map_info.h deleted file mode 100644 index 14bfad6..0000000 --- a/include/corkscrew/map_info.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -/* Process memory map. */ - -#ifndef _CORKSCREW_MAP_INFO_H -#define _CORKSCREW_MAP_INFO_H - -#include <sys/types.h> -#include <stdbool.h> -#include <stdint.h> - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct map_info { - struct map_info* next; - uintptr_t start; - uintptr_t end; - bool is_readable; - bool is_writable; - bool is_executable; - void* data; // arbitrary data associated with the map by the user, initially NULL - char name[]; -} map_info_t; - -/* Loads memory map from /proc/<tid>/maps. */ -map_info_t* load_map_info_list(pid_t tid); - -/* Frees memory map. */ -void free_map_info_list(map_info_t* milist); - -/* Finds the memory map that contains the specified address. */ -const map_info_t* find_map_info(const map_info_t* milist, uintptr_t addr); - -/* Returns true if the addr is in a readable map. */ -bool is_readable_map(const map_info_t* milist, uintptr_t addr); -/* Returns true if the addr is in a writable map. */ -bool is_writable_map(const map_info_t* milist, uintptr_t addr); -/* Returns true if the addr is in an executable map. */ -bool is_executable_map(const map_info_t* milist, uintptr_t addr); - -/* Acquires a reference to the memory map for this process. - * The result is cached and refreshed automatically. - * Make sure to release the map info when done. */ -map_info_t* acquire_my_map_info_list(); - -/* Releases a reference to the map info for this process that was - * previous acquired using acquire_my_map_info_list(). */ -void release_my_map_info_list(map_info_t* milist); - -/* Flushes the cached memory map so the next call to - * acquire_my_map_info_list() gets fresh data. */ -void flush_my_map_info_list(); - -#ifdef __cplusplus -} -#endif - -#endif // _CORKSCREW_MAP_INFO_H diff --git a/include/corkscrew/ptrace.h b/include/corkscrew/ptrace.h deleted file mode 100644 index 76276d8..0000000 --- a/include/corkscrew/ptrace.h +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -/* Useful ptrace() utility functions. */ - -#ifndef _CORKSCREW_PTRACE_H -#define _CORKSCREW_PTRACE_H - -#include <corkscrew/map_info.h> -#include <corkscrew/symbol_table.h> - -#include <sys/types.h> -#include <stdbool.h> -#include <stdint.h> - -#ifdef __cplusplus -extern "C" { -#endif - -/* Stores information about a process that is used for several different - * ptrace() based operations. */ -typedef struct { - map_info_t* map_info_list; -} ptrace_context_t; - -/* Describes how to access memory from a process. */ -typedef struct { - pid_t tid; - const map_info_t* map_info_list; -} memory_t; - -#if __i386__ -/* ptrace() register context. */ -typedef struct pt_regs_x86 { - uint32_t ebx; - uint32_t ecx; - uint32_t edx; - uint32_t esi; - uint32_t edi; - uint32_t ebp; - uint32_t eax; - uint32_t xds; - uint32_t xes; - uint32_t xfs; - uint32_t xgs; - uint32_t orig_eax; - uint32_t eip; - uint32_t xcs; - uint32_t eflags; - uint32_t esp; - uint32_t xss; -} pt_regs_x86_t; -#endif - -#if __mips__ -/* ptrace() GET_REGS context. */ -typedef struct pt_regs_mips { - uint64_t regs[32]; - uint64_t lo; - uint64_t hi; - uint64_t cp0_epc; - uint64_t cp0_badvaddr; - uint64_t cp0_status; - uint64_t cp0_cause; -} pt_regs_mips_t; -#endif - -/* - * Initializes a memory structure for accessing memory from this process. - */ -void init_memory(memory_t* memory, const map_info_t* map_info_list); - -/* - * Initializes a memory structure for accessing memory from another process - * using ptrace(). - */ -void init_memory_ptrace(memory_t* memory, pid_t tid); - -/* - * Reads a word of memory safely. - * If the memory is local, ensures that the address is readable before dereferencing it. - * Returns false and a value of 0xffffffff if the word could not be read. - */ -bool try_get_word(const memory_t* memory, uintptr_t ptr, uint32_t* out_value); - -/* - * Reads a word of memory safely using ptrace(). - * Returns false and a value of 0xffffffff if the word could not be read. - */ -bool try_get_word_ptrace(pid_t tid, uintptr_t ptr, uint32_t* out_value); - -/* - * Loads information needed for examining a remote process using ptrace(). - * The caller must already have successfully attached to the process - * using ptrace(). - * - * The context can be used for any threads belonging to that process - * assuming ptrace() is attached to them before performing the actual - * unwinding. The context can continue to be used to decode backtraces - * even after ptrace() has been detached from the process. - */ -ptrace_context_t* load_ptrace_context(pid_t pid); - -/* - * Frees a ptrace context. - */ -void free_ptrace_context(ptrace_context_t* context); - -/* - * Finds a symbol using ptrace. - * Returns the containing map and information about the symbol, or - * NULL if one or the other is not available. - */ -void find_symbol_ptrace(const ptrace_context_t* context, - uintptr_t addr, const map_info_t** out_map_info, const symbol_t** out_symbol); - -#ifdef __cplusplus -} -#endif - -#endif // _CORKSCREW_PTRACE_H diff --git a/include/corkscrew/symbol_table.h b/include/corkscrew/symbol_table.h deleted file mode 100644 index 4998750..0000000 --- a/include/corkscrew/symbol_table.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -#ifndef _CORKSCREW_SYMBOL_TABLE_H -#define _CORKSCREW_SYMBOL_TABLE_H - -#include <stdint.h> -#include <sys/types.h> - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct { - uintptr_t start; - uintptr_t end; - char* name; -} symbol_t; - -typedef struct { - symbol_t* symbols; - size_t num_symbols; -} symbol_table_t; - -/* - * Loads a symbol table from a given file. - * Returns NULL on error. - */ -symbol_table_t* load_symbol_table(const char* filename); - -/* - * Frees a symbol table. - */ -void free_symbol_table(symbol_table_t* table); - -/* - * Finds a symbol associated with an address in the symbol table. - * Returns NULL if not found. - */ -const symbol_t* find_symbol(const symbol_table_t* table, uintptr_t addr); - -#ifdef __cplusplus -} -#endif - -#endif // _CORKSCREW_SYMBOL_TABLE_H diff --git a/include/cutils/atomic-inline.h b/include/cutils/atomic-inline.h index ae79e00..007a905 100644 --- a/include/cutils/atomic-inline.h +++ b/include/cutils/atomic-inline.h @@ -47,8 +47,12 @@ extern "C" { #include <cutils/atomic-arm64.h> #elif defined(__arm__) #include <cutils/atomic-arm.h> -#elif defined(__i386__) || defined(__x86_64__) +#elif defined(__i386__) #include <cutils/atomic-x86.h> +#elif defined(__x86_64__) +#include <cutils/atomic-x86_64.h> +#elif defined(__mips64) +#include <cutils/atomic-mips64.h> #elif defined(__mips__) #include <cutils/atomic-mips.h> #else diff --git a/include/cutils/atomic-mips.h b/include/cutils/atomic-mips.h index f9d3e25..1ed833d 100644 --- a/include/cutils/atomic-mips.h +++ b/include/cutils/atomic-mips.h @@ -117,23 +117,6 @@ android_atomic_release_cas(int32_t old_value, extern ANDROID_ATOMIC_INLINE int32_t -android_atomic_swap(int32_t new_value, volatile int32_t *ptr) -{ - int32_t prev, status; - do { - __asm__ __volatile__ ( - " move %[status], %[new_value]\n" - " ll %[prev], (%[ptr])\n" - " sc %[status], (%[ptr])\n" - : [prev] "=&r" (prev), [status] "=&r" (status) - : [ptr] "r" (ptr), [new_value] "r" (new_value) - ); - } while (__builtin_expect(status == 0, 0)); - android_memory_barrier(); - return prev; -} - -extern ANDROID_ATOMIC_INLINE int32_t android_atomic_add(int32_t increment, volatile int32_t *ptr) { int32_t prev, status; diff --git a/include/cutils/atomic-mips64.h b/include/cutils/atomic-mips64.h new file mode 100644 index 0000000..99bbe3a --- /dev/null +++ b/include/cutils/atomic-mips64.h @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2010 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. + */ + +#ifndef ANDROID_CUTILS_ATOMIC_MIPS64_H +#define ANDROID_CUTILS_ATOMIC_MIPS64_H + +#include <stdint.h> + +#ifndef ANDROID_ATOMIC_INLINE +#define ANDROID_ATOMIC_INLINE inline __attribute__((always_inline)) +#endif + +extern ANDROID_ATOMIC_INLINE void android_compiler_barrier(void) +{ + __asm__ __volatile__ ("" : : : "memory"); +} + +#if ANDROID_SMP == 0 +extern ANDROID_ATOMIC_INLINE void android_memory_barrier(void) +{ + android_compiler_barrier(); +} +extern ANDROID_ATOMIC_INLINE void android_memory_store_barrier(void) +{ + android_compiler_barrier(); +} +#else +extern ANDROID_ATOMIC_INLINE void android_memory_barrier(void) +{ + __asm__ __volatile__ ("sync" : : : "memory"); +} +extern ANDROID_ATOMIC_INLINE void android_memory_store_barrier(void) +{ + __asm__ __volatile__ ("sync" : : : "memory"); +} +#endif + +extern ANDROID_ATOMIC_INLINE +int32_t android_atomic_acquire_load(volatile const int32_t *ptr) +{ + int32_t value = *ptr; + android_memory_barrier(); + return value; +} + +extern ANDROID_ATOMIC_INLINE +int64_t android_atomic_acquire_load64(volatile const int64_t *ptr) +{ + int64_t value = *ptr; + android_memory_barrier(); + return value; +} + +extern ANDROID_ATOMIC_INLINE +int32_t android_atomic_release_load(volatile const int32_t *ptr) +{ + android_memory_barrier(); + return *ptr; +} + +extern ANDROID_ATOMIC_INLINE +int64_t android_atomic_release_load64(volatile const int64_t *ptr) +{ + android_memory_barrier(); + return *ptr; +} + +extern ANDROID_ATOMIC_INLINE +void android_atomic_acquire_store(int32_t value, volatile int32_t *ptr) +{ + *ptr = value; + android_memory_barrier(); +} + +extern ANDROID_ATOMIC_INLINE +void android_atomic_acquire_store64(int64_t value, volatile int64_t *ptr) +{ + *ptr = value; + android_memory_barrier(); +} + +extern ANDROID_ATOMIC_INLINE +void android_atomic_release_store(int32_t value, volatile int32_t *ptr) +{ + android_memory_barrier(); + *ptr = value; +} + +extern ANDROID_ATOMIC_INLINE +void android_atomic_release_store64(int64_t value, volatile int64_t *ptr) +{ + android_memory_barrier(); + *ptr = value; +} + +extern ANDROID_ATOMIC_INLINE +int android_atomic_cas(int32_t old_value, int32_t new_value, volatile int32_t *ptr) +{ + int32_t prev, status; + do { + __asm__ __volatile__ ( + " ll %[prev], (%[ptr])\n" + " li %[status], 1\n" + " bne %[prev], %[old], 9f\n" + " move %[status], %[new_value]\n" + " sc %[status], (%[ptr])\n" + "9:\n" + : [prev] "=&r" (prev), [status] "=&r" (status) + : [ptr] "r" (ptr), [old] "r" (old_value), [new_value] "r" (new_value) + ); + } while (__builtin_expect(status == 0, 0)); + return prev != old_value; +} + +extern ANDROID_ATOMIC_INLINE +int64_t android_atomic_cas64(int64_t old_value, int64_t new_value, + volatile int64_t *ptr) +{ + return __sync_val_compare_and_swap(ptr, old_value, new_value) != old_value; +} + +extern ANDROID_ATOMIC_INLINE +int android_atomic_acquire_cas(int32_t old_value, + int32_t new_value, + volatile int32_t *ptr) +{ + int status = android_atomic_cas(old_value, new_value, ptr); + android_memory_barrier(); + return status; +} + +extern ANDROID_ATOMIC_INLINE +int64_t android_atomic_acquire_cas64(int64_t old_value, int64_t new_value, + volatile int64_t *ptr) +{ + int status = android_atomic_cas64(old_value, new_value, ptr); + android_memory_barrier(); + return status; +} + +extern ANDROID_ATOMIC_INLINE +int android_atomic_release_cas(int32_t old_value, + int32_t new_value, + volatile int32_t *ptr) +{ + android_memory_barrier(); + return android_atomic_cas(old_value, new_value, ptr); +} + +extern ANDROID_ATOMIC_INLINE +int64_t android_atomic_release_cas64(int64_t old_value, int64_t new_value, + volatile int64_t *ptr) +{ + android_memory_barrier(); + return android_atomic_cas64(old_value, new_value, ptr); +} + +extern ANDROID_ATOMIC_INLINE +int32_t android_atomic_add(int32_t increment, volatile int32_t *ptr) +{ + int32_t prev, status; + android_memory_barrier(); + do { + __asm__ __volatile__ ( + " ll %[prev], (%[ptr])\n" + " addu %[status], %[prev], %[inc]\n" + " sc %[status], (%[ptr])\n" + : [status] "=&r" (status), [prev] "=&r" (prev) + : [ptr] "r" (ptr), [inc] "Ir" (increment) + ); + } while (__builtin_expect(status == 0, 0)); + return prev; +} + +extern ANDROID_ATOMIC_INLINE int32_t +android_atomic_inc(volatile int32_t *addr) +{ + return android_atomic_add(1, addr); +} + +extern ANDROID_ATOMIC_INLINE int32_t +android_atomic_dec(volatile int32_t *addr) +{ + return android_atomic_add(-1, addr); +} + +extern ANDROID_ATOMIC_INLINE int32_t +android_atomic_and(int32_t value, volatile int32_t *ptr) +{ + int32_t prev, status; + android_memory_barrier(); + do { + __asm__ __volatile__ ( + " ll %[prev], (%[ptr])\n" + " and %[status], %[prev], %[value]\n" + " sc %[status], (%[ptr])\n" + : [prev] "=&r" (prev), [status] "=&r" (status) + : [ptr] "r" (ptr), [value] "Ir" (value) + ); + } while (__builtin_expect(status == 0, 0)); + return prev; +} + +extern ANDROID_ATOMIC_INLINE int32_t +android_atomic_or(int32_t value, volatile int32_t *ptr) +{ + int32_t prev, status; + android_memory_barrier(); + do { + __asm__ __volatile__ ( + " ll %[prev], (%[ptr])\n" + " or %[status], %[prev], %[value]\n" + " sc %[status], (%[ptr])\n" + : [prev] "=&r" (prev), [status] "=&r" (status) + : [ptr] "r" (ptr), [value] "Ir" (value) + ); + } while (__builtin_expect(status == 0, 0)); + return prev; +} + +#endif /* ANDROID_CUTILS_ATOMIC_MIPS_H */ diff --git a/include/cutils/atomic-x86_64.h b/include/cutils/atomic-x86_64.h new file mode 100644 index 0000000..5b5c203 --- /dev/null +++ b/include/cutils/atomic-x86_64.h @@ -0,0 +1,226 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef ANDROID_CUTILS_ATOMIC_X86_64_H +#define ANDROID_CUTILS_ATOMIC_X86_64_H + +#include <stdint.h> + +#ifndef ANDROID_ATOMIC_INLINE +#define ANDROID_ATOMIC_INLINE inline __attribute__((always_inline)) +#endif + +extern ANDROID_ATOMIC_INLINE +void android_compiler_barrier(void) +{ + __asm__ __volatile__ ("" : : : "memory"); +} + +#if ANDROID_SMP == 0 +extern ANDROID_ATOMIC_INLINE +void android_memory_barrier(void) +{ + android_compiler_barrier(); +} +extern ANDROID_ATOMIC_INLINE +void android_memory_store_barrier(void) +{ + android_compiler_barrier(); +} +#else +extern ANDROID_ATOMIC_INLINE +void android_memory_barrier(void) +{ + __asm__ __volatile__ ("mfence" : : : "memory"); +} +extern ANDROID_ATOMIC_INLINE +void android_memory_store_barrier(void) +{ + android_compiler_barrier(); +} +#endif + +extern ANDROID_ATOMIC_INLINE +int32_t android_atomic_acquire_load(volatile const int32_t *ptr) +{ + int32_t value = *ptr; + android_compiler_barrier(); + return value; +} + +extern ANDROID_ATOMIC_INLINE +int64_t android_atomic_acquire_load64(volatile const int64_t *ptr) +{ + int64_t value = *ptr; + android_compiler_barrier(); + return value; +} + +extern ANDROID_ATOMIC_INLINE +int32_t android_atomic_release_load(volatile const int32_t *ptr) +{ + android_memory_barrier(); + return *ptr; +} + +extern ANDROID_ATOMIC_INLINE +int64_t android_atomic_release_load64(volatile const int64_t *ptr) +{ + android_memory_barrier(); + return *ptr; +} + +extern ANDROID_ATOMIC_INLINE +void android_atomic_acquire_store(int32_t value, volatile int32_t *ptr) +{ + *ptr = value; + android_memory_barrier(); +} + +extern ANDROID_ATOMIC_INLINE +void android_atomic_acquire_store64(int64_t value, volatile int64_t *ptr) +{ + *ptr = value; + android_memory_barrier(); +} + +extern ANDROID_ATOMIC_INLINE +void android_atomic_release_store(int32_t value, volatile int32_t *ptr) +{ + android_compiler_barrier(); + *ptr = value; +} + +extern ANDROID_ATOMIC_INLINE +void android_atomic_release_store64(int64_t value, volatile int64_t *ptr) +{ + android_compiler_barrier(); + *ptr = value; +} + +extern ANDROID_ATOMIC_INLINE +int android_atomic_cas(int32_t old_value, int32_t new_value, + volatile int32_t *ptr) +{ + int32_t prev; + __asm__ __volatile__ ("lock; cmpxchgl %1, %2" + : "=a" (prev) + : "q" (new_value), "m" (*ptr), "0" (old_value) + : "memory"); + return prev != old_value; +} + +extern ANDROID_ATOMIC_INLINE +int64_t android_atomic_cas64(int64_t old_value, int64_t new_value, + volatile int64_t *ptr) +{ + int64_t prev; + __asm__ __volatile__ ("lock; cmpxchgq %1, %2" + : "=a" (prev) + : "q" (new_value), "m" (*ptr), "0" (old_value) + : "memory"); + return prev != old_value; +} + +extern ANDROID_ATOMIC_INLINE +int android_atomic_acquire_cas(int32_t old_value, int32_t new_value, + volatile int32_t *ptr) +{ + /* Loads are not reordered with other loads. */ + return android_atomic_cas(old_value, new_value, ptr); +} + +extern ANDROID_ATOMIC_INLINE +int64_t android_atomic_acquire_cas64(int64_t old_value, int64_t new_value, + volatile int64_t *ptr) +{ + /* Loads are not reordered with other loads. */ + return android_atomic_cas64(old_value, new_value, ptr); +} + +extern ANDROID_ATOMIC_INLINE +int android_atomic_release_cas(int32_t old_value, int32_t new_value, + volatile int32_t *ptr) +{ + /* Stores are not reordered with other stores. */ + return android_atomic_cas(old_value, new_value, ptr); +} + +extern ANDROID_ATOMIC_INLINE +int64_t android_atomic_release_cas64(int64_t old_value, int64_t new_value, + volatile int64_t *ptr) +{ + /* Stores are not reordered with other stores. */ + return android_atomic_cas64(old_value, new_value, ptr); +} + +extern ANDROID_ATOMIC_INLINE +int32_t android_atomic_add(int32_t increment, volatile int32_t *ptr) +{ + __asm__ __volatile__ ("lock; xaddl %0, %1" + : "+r" (increment), "+m" (*ptr) + : : "memory"); + /* increment now holds the old value of *ptr */ + return increment; +} + +extern ANDROID_ATOMIC_INLINE +int32_t android_atomic_inc(volatile int32_t *addr) +{ + return android_atomic_add(1, addr); +} + +extern ANDROID_ATOMIC_INLINE +int32_t android_atomic_dec(volatile int32_t *addr) +{ + return android_atomic_add(-1, addr); +} + +extern ANDROID_ATOMIC_INLINE +int32_t android_atomic_and(int32_t value, volatile int32_t *ptr) +{ + int32_t prev, status; + do { + prev = *ptr; + status = android_atomic_cas(prev, prev & value, ptr); + } while (__builtin_expect(status != 0, 0)); + return prev; +} + +extern ANDROID_ATOMIC_INLINE +int32_t android_atomic_or(int32_t value, volatile int32_t *ptr) +{ + int32_t prev, status; + do { + prev = *ptr; + status = android_atomic_cas(prev, prev | value, ptr); + } while (__builtin_expect(status != 0, 0)); + return prev; +} + +#endif /* ANDROID_CUTILS_ATOMIC_X86_64_H */ diff --git a/include/cutils/bitops.h b/include/cutils/bitops.h index c26dc54..045830d 100644 --- a/include/cutils/bitops.h +++ b/include/cutils/bitops.h @@ -59,7 +59,7 @@ static inline void bitmask_init(unsigned int *bitmask, int num_bits) static inline int bitmask_ffz(unsigned int *bitmask, int num_bits) { int bit, result; - unsigned int i; + size_t i; for (i = 0; i < BITS_TO_WORDS(num_bits); i++) { bit = ffs(~bitmask[i]); @@ -77,7 +77,7 @@ static inline int bitmask_ffz(unsigned int *bitmask, int num_bits) static inline int bitmask_weight(unsigned int *bitmask, int num_bits) { - int i; + size_t i; int weight = 0; for (i = 0; i < BITS_TO_WORDS(num_bits); i++) diff --git a/include/cutils/debugger.h b/include/cutils/debugger.h index 4eda523..ae6bfc4 100644 --- a/include/cutils/debugger.h +++ b/include/cutils/debugger.h @@ -23,7 +23,11 @@ extern "C" { #endif +#if __LP64__ +#define DEBUGGER_SOCKET_NAME "android:debuggerd64" +#else #define DEBUGGER_SOCKET_NAME "android:debuggerd" +#endif typedef enum { // dump a crash @@ -38,6 +42,7 @@ typedef struct { debugger_action_t action; pid_t tid; uintptr_t abort_msg_address; + int32_t original_si_code; } debugger_msg_t; /* Dumps a process backtrace, registers, and stack to a tombstone file (requires root). diff --git a/include/cutils/fs.h b/include/cutils/fs.h index d1d4cf2..70f0b92 100644 --- a/include/cutils/fs.h +++ b/include/cutils/fs.h @@ -18,6 +18,7 @@ #define __CUTILS_FS_H #include <sys/types.h> +#include <unistd.h> /* * TEMP_FAILURE_RETRY is defined by some, but not all, versions of diff --git a/include/cutils/klog.h b/include/cutils/klog.h index ba728ac..3635e89 100644 --- a/include/cutils/klog.h +++ b/include/cutils/klog.h @@ -18,22 +18,31 @@ #define _CUTILS_KLOG_H_ #include <sys/cdefs.h> +#include <stdarg.h> __BEGIN_DECLS void klog_init(void); +int klog_get_level(void); void klog_set_level(int level); void klog_close(void); void klog_write(int level, const char *fmt, ...) __attribute__ ((format(printf, 2, 3))); +void klog_vwrite(int level, const char *fmt, va_list ap); __END_DECLS -#define KLOG_ERROR(tag,x...) klog_write(3, "<3>" tag ": " x) -#define KLOG_WARNING(tag,x...) klog_write(4, "<4>" tag ": " x) -#define KLOG_NOTICE(tag,x...) klog_write(5, "<5>" tag ": " x) -#define KLOG_INFO(tag,x...) klog_write(6, "<6>" tag ": " x) -#define KLOG_DEBUG(tag,x...) klog_write(7, "<7>" tag ": " x) +#define KLOG_ERROR_LEVEL 3 +#define KLOG_WARNING_LEVEL 4 +#define KLOG_NOTICE_LEVEL 5 +#define KLOG_INFO_LEVEL 6 +#define KLOG_DEBUG_LEVEL 7 + +#define KLOG_ERROR(tag,x...) klog_write(KLOG_ERROR_LEVEL, "<3>" tag ": " x) +#define KLOG_WARNING(tag,x...) klog_write(KLOG_WARNING_LEVEL, "<4>" tag ": " x) +#define KLOG_NOTICE(tag,x...) klog_write(KLOG_NOTICE_LEVEL, "<5>" tag ": " x) +#define KLOG_INFO(tag,x...) klog_write(KLOG_INFO_LEVEL, "<6>" tag ": " x) +#define KLOG_DEBUG(tag,x...) klog_write(KLOG_DEBUG_LEVEL, "<7>" tag ": " x) #define KLOG_DEFAULT_LEVEL 3 /* messages <= this level are logged */ diff --git a/include/cutils/list.h b/include/cutils/list.h index 945729a..4ba2cfd 100644 --- a/include/cutils/list.h +++ b/include/cutils/list.h @@ -44,10 +44,10 @@ struct listnode #define list_for_each_reverse(node, list) \ for (node = (list)->prev; node != (list); node = node->prev) -#define list_for_each_safe(node, next, list) \ - for (node = (list)->next, next = node->next; \ +#define list_for_each_safe(node, n, list) \ + for (node = (list)->next, n = node->next; \ node != (list); \ - node = next, next = node->next) + node = n, n = node->next) static inline void list_init(struct listnode *node) { @@ -63,6 +63,14 @@ static inline void list_add_tail(struct listnode *head, struct listnode *item) head->prev = item; } +static inline void list_add_head(struct listnode *head, struct listnode *item) +{ + item->next = head->next; + item->prev = head; + head->next->prev = item; + head->next = item; +} + static inline void list_remove(struct listnode *item) { item->next->prev = item->prev; diff --git a/include/cutils/misc.h b/include/cutils/misc.h index 2c48dfa..0de505f 100644 --- a/include/cutils/misc.h +++ b/include/cutils/misc.h @@ -28,13 +28,6 @@ extern "C" { */ extern void *load_file(const char *fn, unsigned *sz); - /* Connects your process to the system debugger daemon - * so that on a crash it may be logged or interactively - * debugged (depending on system settings). - */ -extern void debuggerd_connect(void); - - /* This is the range of UIDs (and GIDs) that are reserved * for assigning to applications. */ diff --git a/include/cutils/properties.h b/include/cutils/properties.h index 2c70165..798db8b 100644 --- a/include/cutils/properties.h +++ b/include/cutils/properties.h @@ -20,6 +20,7 @@ #include <sys/cdefs.h> #include <stddef.h> #include <sys/system_properties.h> +#include <stdint.h> #ifdef __cplusplus extern "C" { @@ -44,6 +45,64 @@ extern "C" { */ int property_get(const char *key, char *value, const char *default_value); +/* property_get_bool: returns the value of key coerced into a +** boolean. If the property is not set, then the default value is returned. +** +* The following is considered to be true (1): +** "1", "true", "y", "yes", "on" +** +** The following is considered to be false (0): +** "0", "false", "n", "no", "off" +** +** The conversion is whitespace-sensitive (e.g. " off" will not be false). +** +** If no property with this key is set (or the key is NULL) or the boolean +** conversion fails, the default value is returned. +**/ +int8_t property_get_bool(const char *key, int8_t default_value); + +/* property_get_int64: returns the value of key truncated and coerced into a +** int64_t. If the property is not set, then the default value is used. +** +** The numeric conversion is identical to strtoimax with the base inferred: +** - All digits up to the first non-digit characters are read +** - The longest consecutive prefix of digits is converted to a long +** +** Valid strings of digits are: +** - An optional sign character + or - +** - An optional prefix indicating the base (otherwise base 10 is assumed) +** -- 0 prefix is octal +** -- 0x / 0X prefix is hex +** +** Leading/trailing whitespace is ignored. Overflow/underflow will cause +** numeric conversion to fail. +** +** If no property with this key is set (or the key is NULL) or the numeric +** conversion fails, the default value is returned. +**/ +int64_t property_get_int64(const char *key, int64_t default_value); + +/* property_get_int32: returns the value of key truncated and coerced into an +** int32_t. If the property is not set, then the default value is used. +** +** The numeric conversion is identical to strtoimax with the base inferred: +** - All digits up to the first non-digit characters are read +** - The longest consecutive prefix of digits is converted to a long +** +** Valid strings of digits are: +** - An optional sign character + or - +** - An optional prefix indicating the base (otherwise base 10 is assumed) +** -- 0 prefix is octal +** -- 0x / 0X prefix is hex +** +** Leading/trailing whitespace is ignored. Overflow/underflow will cause +** numeric conversion to fail. +** +** If no property with this key is set (or the key is NULL) or the numeric +** conversion fails, the default value is returned. +**/ +int32_t property_get_int32(const char *key, int32_t default_value); + /* property_set: returns 0 on success, < 0 on failure */ int property_set(const char *key, const char *value); diff --git a/include/cutils/sockets.h b/include/cutils/sockets.h index 19cae0c..daf43ec 100644 --- a/include/cutils/sockets.h +++ b/include/cutils/sockets.h @@ -86,6 +86,8 @@ static inline int android_get_control_socket(const char *name) extern int socket_loopback_client(int port, int type); extern int socket_network_client(const char *host, int port, int type); +extern int socket_network_client_timeout(const char *host, int port, int type, + int timeout); extern int socket_loopback_server(int port, int type); extern int socket_local_server(const char *name, int namespaceId, int type); extern int socket_local_server_bind(int s, const char *name, int namespaceId); diff --git a/include/cutils/trace.h b/include/cutils/trace.h index 1c8f107..fd9bc6a 100644 --- a/include/cutils/trace.h +++ b/include/cutils/trace.h @@ -17,13 +17,15 @@ #ifndef _LIBS_CUTILS_TRACE_H #define _LIBS_CUTILS_TRACE_H +#include <inttypes.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> #include <sys/cdefs.h> #include <sys/types.h> -#include <stdint.h> -#include <stdbool.h> #include <unistd.h> -#include <cutils/compiler.h> +#include <cutils/compiler.h> #ifdef ANDROID_SMP #include <cutils/atomic-inline.h> #else @@ -67,7 +69,8 @@ __BEGIN_DECLS #define ATRACE_TAG_RESOURCES (1<<13) #define ATRACE_TAG_DALVIK (1<<14) #define ATRACE_TAG_RS (1<<15) -#define ATRACE_TAG_LAST ATRACE_TAG_RS +#define ATRACE_TAG_BIONIC (1<<16) +#define ATRACE_TAG_LAST ATRACE_TAG_BIONIC // Reserved for initialization. #define ATRACE_TAG_NOT_READY (1LL<<63) @@ -217,8 +220,8 @@ static inline void atrace_async_begin(uint64_t tag, const char* name, char buf[ATRACE_MESSAGE_LENGTH]; size_t len; - len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "S|%d|%s|%d", getpid(), - name, cookie); + len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "S|%d|%s|%" PRId32, + getpid(), name, cookie); write(atrace_marker_fd, buf, len); } } @@ -235,8 +238,8 @@ static inline void atrace_async_end(uint64_t tag, const char* name, char buf[ATRACE_MESSAGE_LENGTH]; size_t len; - len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "F|%d|%s|%d", getpid(), - name, cookie); + len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "F|%d|%s|%" PRId32, + getpid(), name, cookie); write(atrace_marker_fd, buf, len); } } @@ -253,7 +256,7 @@ static inline void atrace_int(uint64_t tag, const char* name, int32_t value) char buf[ATRACE_MESSAGE_LENGTH]; size_t len; - len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "C|%d|%s|%d", + len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "C|%d|%s|%" PRId32, getpid(), name, value); write(atrace_marker_fd, buf, len); } @@ -270,7 +273,7 @@ static inline void atrace_int64(uint64_t tag, const char* name, int64_t value) char buf[ATRACE_MESSAGE_LENGTH]; size_t len; - len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "C|%d|%s|%lld", + len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "C|%d|%s|%" PRId64, getpid(), name, value); write(atrace_marker_fd, buf, len); } diff --git a/include/log/log.h b/include/log/log.h index 7f952ff..ace12d6 100644 --- a/include/log/log.h +++ b/include/log/log.h @@ -73,10 +73,11 @@ extern "C" { * Simplified macro to send a verbose log message using the current LOG_TAG. */ #ifndef ALOGV +#define __ALOGV(...) ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) #if LOG_NDEBUG -#define ALOGV(...) ((void)0) +#define ALOGV(...) do { if (0) { __ALOGV(__VA_ARGS__); } } while (0) #else -#define ALOGV(...) ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) +#define ALOGV(...) __ALOGV(__VA_ARGS__) #endif #endif @@ -202,10 +203,11 @@ extern "C" { * Simplified macro to send a verbose system log message using the current LOG_TAG. */ #ifndef SLOGV +#define __SLOGV(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) #if LOG_NDEBUG -#define SLOGV(...) ((void)0) +#define SLOGV(...) do { if (0) { __SLOGV(__VA_ARGS__); } } while (0) #else -#define SLOGV(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) +#define SLOGV(...) __SLOGV(__VA_ARGS__) #endif #endif @@ -284,10 +286,11 @@ extern "C" { * Simplified macro to send a verbose radio log message using the current LOG_TAG. */ #ifndef RLOGV +#define __RLOGV(...) ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) #if LOG_NDEBUG -#define RLOGV(...) ((void)0) +#define RLOGV(...) do { if (0) { __RLOGV(__VA_ARGS__); } } while (0) #else -#define RLOGV(...) ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) +#define RLOGV(...) __RLOGV(__VA_ARGS__) #endif #endif @@ -488,7 +491,7 @@ typedef enum { #endif #ifndef LOG_EVENT_STRING #define LOG_EVENT_STRING(_tag, _value) \ - ((void) 0) /* not implemented -- must combine len with string */ + (void) __android_log_bswrite(_tag, _value); #endif /* TODO: something for LIST */ @@ -547,6 +550,7 @@ typedef enum log_id { LOG_ID_RADIO = 1, LOG_ID_EVENTS = 2, LOG_ID_SYSTEM = 3, + LOG_ID_CRASH = 4, LOG_ID_MAX } log_id_t; @@ -557,7 +561,11 @@ typedef enum log_id { * Send a simple string to the log. */ int __android_log_buf_write(int bufID, int prio, const char *tag, const char *text); -int __android_log_buf_print(int bufID, int prio, const char *tag, const char *fmt, ...); +int __android_log_buf_print(int bufID, int prio, const char *tag, const char *fmt, ...) +#if defined(__GNUC__) + __attribute__((__format__(printf, 4, 5))) +#endif + ; #ifdef __cplusplus } diff --git a/include/log/log_read.h b/include/log/log_read.h index 861c192..946711a 100644 --- a/include/log/log_read.h +++ b/include/log/log_read.h @@ -17,23 +17,45 @@ #ifndef _LIBS_LOG_LOG_READ_H #define _LIBS_LOG_LOG_READ_H +#include <stdint.h> #include <time.h> +/* struct log_time is a wire-format variant of struct timespec */ #define NS_PER_SEC 1000000000ULL + #ifdef __cplusplus -struct log_time : public timespec { + +// NB: do NOT define a copy constructor. This will result in structure +// no longer being compatible with pass-by-value which is desired +// efficient behavior. Also, pass-by-reference breaks C/C++ ABI. +struct log_time { public: - log_time(timespec &T) + uint32_t tv_sec; // good to Feb 5 2106 + uint32_t tv_nsec; + + static const uint32_t tv_sec_max = 0xFFFFFFFFUL; + static const uint32_t tv_nsec_max = 999999999UL; + + log_time(const timespec &T) { tv_sec = T.tv_sec; tv_nsec = T.tv_nsec; } - log_time(void) + log_time(uint32_t sec, uint32_t nsec) + { + tv_sec = sec; + tv_nsec = nsec; + } + static const timespec EPOCH; + log_time() { } log_time(clockid_t id) { - clock_gettime(id, (timespec *) this); + timespec T; + clock_gettime(id, &T); + tv_sec = T.tv_sec; + tv_nsec = T.tv_nsec; } log_time(const char *T) { @@ -41,9 +63,12 @@ public: tv_sec = c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24); tv_nsec = c[4] | (c[5] << 8) | (c[6] << 16) | (c[7] << 24); } + + // timespec bool operator== (const timespec &T) const { - return (tv_sec == T.tv_sec) && (tv_nsec == T.tv_nsec); + return (tv_sec == static_cast<uint32_t>(T.tv_sec)) + && (tv_nsec == static_cast<uint32_t>(T.tv_nsec)); } bool operator!= (const timespec &T) const { @@ -51,8 +76,9 @@ public: } bool operator< (const timespec &T) const { - return (tv_sec < T.tv_sec) - || ((tv_sec == T.tv_sec) && (tv_nsec < T.tv_nsec)); + return (tv_sec < static_cast<uint32_t>(T.tv_sec)) + || ((tv_sec == static_cast<uint32_t>(T.tv_sec)) + && (tv_nsec < static_cast<uint32_t>(T.tv_nsec))); } bool operator>= (const timespec &T) const { @@ -60,20 +86,73 @@ public: } bool operator> (const timespec &T) const { + return (tv_sec > static_cast<uint32_t>(T.tv_sec)) + || ((tv_sec == static_cast<uint32_t>(T.tv_sec)) + && (tv_nsec > static_cast<uint32_t>(T.tv_nsec))); + } + bool operator<= (const timespec &T) const + { + return !(*this > T); + } + log_time operator-= (const timespec &T); + log_time operator- (const timespec &T) const + { + log_time local(*this); + return local -= T; + } + + // log_time + bool operator== (const log_time &T) const + { + return (tv_sec == T.tv_sec) && (tv_nsec == T.tv_nsec); + } + bool operator!= (const log_time &T) const + { + return !(*this == T); + } + bool operator< (const log_time &T) const + { + return (tv_sec < T.tv_sec) + || ((tv_sec == T.tv_sec) && (tv_nsec < T.tv_nsec)); + } + bool operator>= (const log_time &T) const + { + return !(*this < T); + } + bool operator> (const log_time &T) const + { return (tv_sec > T.tv_sec) || ((tv_sec == T.tv_sec) && (tv_nsec > T.tv_nsec)); } - bool operator<= (const timespec &T) const + bool operator<= (const log_time &T) const { return !(*this > T); } - uint64_t nsec(void) const + log_time operator-= (const log_time &T); + log_time operator- (const log_time &T) const + { + log_time local(*this); + return local -= T; + } + + uint64_t nsec() const { return static_cast<uint64_t>(tv_sec) * NS_PER_SEC + tv_nsec; } -}; + + static const char default_format[]; + + // Add %#q for the fraction of a second to the standard library functions + char *strptime(const char *s, const char *format = default_format); +} __attribute__((__packed__)); + #else -typedef struct timespec log_time; + +typedef struct log_time { + uint32_t tv_sec; + uint32_t tv_nsec; +} __attribute__((__packed__)) log_time; + #endif #endif /* define _LIBS_LOG_LOG_READ_H */ diff --git a/include/log/logd.h b/include/log/logd.h index 379c373..2e6f220 100644 --- a/include/log/logd.h +++ b/include/log/logd.h @@ -41,6 +41,7 @@ extern "C" { int __android_log_bwrite(int32_t tag, const void *payload, size_t len); int __android_log_btwrite(int32_t tag, char type, const void *payload, size_t len); +int __android_log_bswrite(int32_t tag, const char *payload); #ifdef __cplusplus } diff --git a/include/log/logger.h b/include/log/logger.h index 966397a..53be1d3 100644 --- a/include/log/logger.h +++ b/include/log/logger.h @@ -12,6 +12,7 @@ #include <stdint.h> #include <log/log.h> +#include <log/log_read.h> #ifdef __cplusplus extern "C" { @@ -30,12 +31,12 @@ struct logger_entry { int32_t sec; /* seconds since Epoch */ int32_t nsec; /* nanoseconds */ char msg[0]; /* the entry's payload */ -}; +} __attribute__((__packed__)); /* * The userspace structure for version 2 of the logger_entry ABI. * This structure is returned to userspace if ioctl(LOGGER_SET_VERSION) - * is called with version==2 + * is called with version==2; or used with the user space log daemon. */ struct logger_entry_v2 { uint16_t len; /* length of the payload */ @@ -46,13 +47,23 @@ struct logger_entry_v2 { int32_t nsec; /* nanoseconds */ uint32_t euid; /* effective UID of logger */ char msg[0]; /* the entry's payload */ -}; +} __attribute__((__packed__)); + +struct logger_entry_v3 { + uint16_t len; /* length of the payload */ + uint16_t hdr_size; /* sizeof(struct logger_entry_v3) */ + int32_t pid; /* generating process's pid */ + int32_t tid; /* generating process's tid */ + int32_t sec; /* seconds since Epoch */ + int32_t nsec; /* nanoseconds */ + uint32_t lid; /* log id of the payload */ + char msg[0]; /* the entry's payload */ +} __attribute__((__packed__)); /* * The maximum size of the log entry payload that can be - * written to the kernel logger driver. An attempt to write - * more than this amount to /dev/log/* will result in a - * truncated log entry. + * written to the logger. An attempt to write more than + * this amount will result in a truncated log entry. */ #define LOGGER_ENTRY_MAX_PAYLOAD 4076 @@ -68,13 +79,10 @@ struct logger_entry_v2 { struct log_msg { union { unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1]; - struct logger_entry_v2 entry; + struct logger_entry_v3 entry; + struct logger_entry_v3 entry_v3; struct logger_entry_v2 entry_v2; struct logger_entry entry_v1; - struct { - unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1]; - log_id_t id; - } extra; } __attribute__((aligned(4))); #ifdef __cplusplus /* Matching log_time operators */ @@ -106,21 +114,21 @@ struct log_msg { { return !(*this > T); } - uint64_t nsec(void) const + uint64_t nsec() const { return static_cast<uint64_t>(entry.sec) * NS_PER_SEC + entry.nsec; } /* packet methods */ - log_id_t id(void) + log_id_t id() { - return extra.id; + return (log_id_t) entry.lid; } - char *msg(void) + char *msg() { return entry.hdr_size ? (char *) buf + entry.hdr_size : entry_v1.msg; } - unsigned int len(void) + unsigned int len() { return (entry.hdr_size ? entry.hdr_size : sizeof(entry_v1)) + entry.len; } @@ -132,15 +140,26 @@ struct logger; log_id_t android_logger_get_id(struct logger *logger); int android_logger_clear(struct logger *logger); -int android_logger_get_log_size(struct logger *logger); -int android_logger_get_log_readable_size(struct logger *logger); +long android_logger_get_log_size(struct logger *logger); +int android_logger_set_log_size(struct logger *logger, unsigned long size); +long android_logger_get_log_readable_size(struct logger *logger); int android_logger_get_log_version(struct logger *logger); struct logger_list; +ssize_t android_logger_get_statistics(struct logger_list *logger_list, + char *buf, size_t len); +ssize_t android_logger_get_prune_list(struct logger_list *logger_list, + char *buf, size_t len); +int android_logger_set_prune_list(struct logger_list *logger_list, + char *buf, size_t len); + struct logger_list *android_logger_list_alloc(int mode, unsigned int tail, pid_t pid); +struct logger_list *android_logger_list_alloc_time(int mode, + log_time start, + pid_t pid); void android_logger_list_free(struct logger_list *logger_list); /* In the purest sense, the following two are orthogonal interfaces */ int android_logger_list_read(struct logger_list *logger_list, diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h index 0ed0d78..d8e938e 100644 --- a/include/private/android_filesystem_config.h +++ b/include/private/android_filesystem_config.h @@ -76,6 +76,8 @@ #define AID_SDCARD_PICS 1033 /* external storage photos access */ #define AID_SDCARD_AV 1034 /* external storage audio/video access */ #define AID_SDCARD_ALL 1035 /* access all users external storage */ +#define AID_LOGD 1036 /* log daemon */ +#define AID_SHARED_RELRO 1037 /* creator of shared GNU RELRO files */ #define AID_SHELL 2000 /* adb and debug shell user */ #define AID_CACHE 2001 /* cache access */ @@ -92,6 +94,7 @@ #define AID_NET_BW_ACCT 3007 /* change bandwidth statistics accounting */ #define AID_NET_BT_STACK 3008 /* bluetooth: access config files */ +#define AID_EVERYBODY 9997 /* shared between all apps in the same profile */ #define AID_MISC 9998 /* access to misc storage */ #define AID_NOBODY 9999 @@ -151,6 +154,8 @@ static const struct android_id_info android_ids[] = { { "sdcard_pics", AID_SDCARD_PICS, }, { "sdcard_av", AID_SDCARD_AV, }, { "sdcard_all", AID_SDCARD_ALL, }, + { "logd", AID_LOGD, }, + { "shared_relro", AID_SHARED_RELRO, }, { "shell", AID_SHELL, }, { "cache", AID_CACHE, }, @@ -165,6 +170,7 @@ static const struct android_id_info android_ids[] = { { "net_bw_acct", AID_NET_BW_ACCT, }, { "net_bt_stack", AID_NET_BT_STACK, }, + { "everybody", AID_EVERYBODY, }, { "misc", AID_MISC, }, { "nobody", AID_NOBODY, }, }; @@ -196,6 +202,7 @@ static const struct fs_path_config android_dirs[] = { { 00771, AID_SHELL, AID_SHELL, 0, "data/local" }, { 01771, AID_SYSTEM, AID_MISC, 0, "data/misc" }, { 00770, AID_DHCP, AID_DHCP, 0, "data/misc/dhcp" }, + { 00771, AID_SHARED_RELRO, AID_SHARED_RELRO, 0, "data/misc/shared_relro" }, { 00775, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media" }, { 00775, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media/Music" }, { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data" }, @@ -237,7 +244,7 @@ static const struct fs_path_config android_files[] = { /* the following five files are INTENTIONALLY set-uid, but they * are NOT included on user builds. */ - { 06755, AID_ROOT, AID_ROOT, 0, "system/xbin/su" }, + { 04750, AID_ROOT, AID_SHELL, 0, "system/xbin/su" }, { 06755, AID_ROOT, AID_ROOT, 0, "system/xbin/librank" }, { 06755, AID_ROOT, AID_ROOT, 0, "system/xbin/procrank" }, { 06755, AID_ROOT, AID_ROOT, 0, "system/xbin/procmem" }, @@ -249,6 +256,7 @@ static const struct fs_path_config android_files[] = { { 00755, AID_ROOT, AID_SHELL, 0, "system/bin/*" }, { 00755, AID_ROOT, AID_ROOT, 0, "system/lib/valgrind/*" }, + { 00755, AID_ROOT, AID_ROOT, 0, "system/lib64/valgrind/*" }, { 00755, AID_ROOT, AID_SHELL, 0, "system/xbin/*" }, { 00755, AID_ROOT, AID_SHELL, 0, "system/vendor/bin/*" }, { 00755, AID_ROOT, AID_SHELL, 0, "vendor/bin/*" }, diff --git a/include/system/audio.h b/include/system/audio.h index aa7ac02..8838e71 100644 --- a/include/system/audio.h +++ b/include/system/audio.h @@ -434,6 +434,14 @@ typedef struct { static const audio_offload_info_t AUDIO_INFO_INITIALIZER = { version: AUDIO_OFFLOAD_INFO_VERSION_CURRENT, size: sizeof(audio_offload_info_t), + sample_rate: 0, + channel_mask: 0, + format: AUDIO_FORMAT_DEFAULT, + stream_type: AUDIO_STREAM_VOICE_CALL, + bit_rate: 0, + duration_us: 0, + has_video: false, + is_streaming: false }; static inline bool audio_is_output_device(audio_devices_t device) @@ -471,12 +479,16 @@ static inline bool audio_is_a2dp_device(audio_devices_t device) static inline bool audio_is_bluetooth_sco_device(audio_devices_t device) { - device &= ~AUDIO_DEVICE_BIT_IN; - if ((popcount(device) == 1) && (device & (AUDIO_DEVICE_OUT_ALL_SCO | - AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET))) - return true; - else - return false; + if ((device & AUDIO_DEVICE_BIT_IN) == 0) { + if ((popcount(device) == 1) && ((device & ~AUDIO_DEVICE_OUT_ALL_SCO) == 0)) + return true; + } else { + device &= ~AUDIO_DEVICE_BIT_IN; + if ((popcount(device) == 1) && ((device & ~AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) == 0)) + return true; + } + + return false; } static inline bool audio_is_usb_device(audio_devices_t device) diff --git a/include/system/window.h b/include/system/window.h index 588f9c6..31f202f 100644 --- a/include/system/window.h +++ b/include/system/window.h @@ -26,6 +26,13 @@ #include <system/graphics.h> #include <unistd.h> +#ifndef __UNUSED +#define __UNUSED __attribute__((__unused__)) +#endif +#ifndef __deprecated +#define __deprecated __attribute__((__deprecated__)) +#endif + __BEGIN_DECLS /*****************************************************************************/ @@ -89,10 +96,10 @@ typedef struct ANativeWindowBuffer // Implement the methods that sp<ANativeWindowBuffer> expects so that it // can be used to automatically refcount ANativeWindowBuffer's. - void incStrong(const void* id) const { + void incStrong(const void* /*id*/) const { common.incRef(const_cast<android_native_base_t*>(&common)); } - void decStrong(const void* id) const { + void decStrong(const void* /*id*/) const { common.decRef(const_cast<android_native_base_t*>(&common)); } #endif @@ -352,10 +359,10 @@ struct ANativeWindow /* Implement the methods that sp<ANativeWindow> expects so that it can be used to automatically refcount ANativeWindow's. */ - void incStrong(const void* id) const { + void incStrong(const void* /*id*/) const { common.incRef(const_cast<android_native_base_t*>(&common)); } - void decStrong(const void* id) const { + void decStrong(const void* /*id*/) const { common.decRef(const_cast<android_native_base_t*>(&common)); } #endif @@ -582,7 +589,7 @@ struct ANativeWindow * android_native_window_t is deprecated. */ typedef struct ANativeWindow ANativeWindow; -typedef struct ANativeWindow android_native_window_t; +typedef struct ANativeWindow android_native_window_t __deprecated; /* * native_window_set_usage(..., usage) @@ -603,13 +610,19 @@ static inline int native_window_set_usage( /* deprecated. Always returns 0. Don't call. */ static inline int native_window_connect( - struct ANativeWindow* window, int api) { + struct ANativeWindow* window __UNUSED, int api __UNUSED) __deprecated; + +static inline int native_window_connect( + struct ANativeWindow* window __UNUSED, int api __UNUSED) { return 0; } /* deprecated. Always returns 0. Don't call. */ static inline int native_window_disconnect( - struct ANativeWindow* window, int api) { + struct ANativeWindow* window __UNUSED, int api __UNUSED) __deprecated; + +static inline int native_window_disconnect( + struct ANativeWindow* window __UNUSED, int api __UNUSED) { return 0; } @@ -664,6 +677,10 @@ static inline int native_window_set_post_transform_crop( */ static inline int native_window_set_active_rect( struct ANativeWindow* window, + android_native_rect_t const * active_rect) __deprecated; + +static inline int native_window_set_active_rect( + struct ANativeWindow* window, android_native_rect_t const * active_rect) { return native_window_set_post_transform_crop(window, active_rect); @@ -691,6 +708,10 @@ static inline int native_window_set_buffer_count( */ static inline int native_window_set_buffers_geometry( struct ANativeWindow* window, + int w, int h, int format) __deprecated; + +static inline int native_window_set_buffers_geometry( + struct ANativeWindow* window, int w, int h, int format) { return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_GEOMETRY, diff --git a/include/sysutils/FrameworkListener.h b/include/sysutils/FrameworkListener.h index f1a4b43..18049cd 100644 --- a/include/sysutils/FrameworkListener.h +++ b/include/sysutils/FrameworkListener.h @@ -36,6 +36,7 @@ private: public: FrameworkListener(const char *socketName); FrameworkListener(const char *socketName, bool withSeq); + FrameworkListener(int sock); virtual ~FrameworkListener() {} protected: diff --git a/include/sysutils/SocketListener.h b/include/sysutils/SocketListener.h index c204a0f..bc93b86 100644 --- a/include/sysutils/SocketListener.h +++ b/include/sysutils/SocketListener.h @@ -38,6 +38,7 @@ public: virtual ~SocketListener(); int startListener(); + int startListener(int backlog); int stopListener(); void sendBroadcast(int code, const char *msg, bool addErrno); diff --git a/include/utils/Errors.h b/include/utils/Errors.h index 0b75b19..46173db 100644 --- a/include/utils/Errors.h +++ b/include/utils/Errors.h @@ -41,23 +41,23 @@ typedef int32_t status_t; #ifdef _WIN32 # undef NO_ERROR #endif - + enum { OK = 0, // Everything's swell. NO_ERROR = 0, // No errors. - - UNKNOWN_ERROR = 0x80000000, + + UNKNOWN_ERROR = (-2147483647-1), // INT32_MIN value NO_MEMORY = -ENOMEM, INVALID_OPERATION = -ENOSYS, BAD_VALUE = -EINVAL, - BAD_TYPE = 0x80000001, + BAD_TYPE = (UNKNOWN_ERROR + 1), NAME_NOT_FOUND = -ENOENT, PERMISSION_DENIED = -EPERM, NO_INIT = -ENODEV, ALREADY_EXISTS = -EEXIST, DEAD_OBJECT = -EPIPE, - FAILED_TRANSACTION = 0x80000002, + FAILED_TRANSACTION = (UNKNOWN_ERROR + 2), JPARKS_BROKE_IT = -EPIPE, #if !defined(HAVE_MS_C_RUNTIME) BAD_INDEX = -EOVERFLOW, @@ -67,12 +67,12 @@ enum { UNKNOWN_TRANSACTION = -EBADMSG, #else BAD_INDEX = -E2BIG, - NOT_ENOUGH_DATA = 0x80000003, - WOULD_BLOCK = 0x80000004, - TIMED_OUT = 0x80000005, - UNKNOWN_TRANSACTION = 0x80000006, + NOT_ENOUGH_DATA = (UNKNOWN_ERROR + 3), + WOULD_BLOCK = (UNKNOWN_ERROR + 4), + TIMED_OUT = (UNKNOWN_ERROR + 5), + UNKNOWN_TRANSACTION = (UNKNOWN_ERROR + 6), #endif - FDS_NOT_ALLOWED = 0x80000007, + FDS_NOT_ALLOWED = (UNKNOWN_ERROR + 7), }; // Restore define; enumeration is in "android" namespace, so the value defined diff --git a/include/utils/Functor.h b/include/utils/Functor.h index e24ded4..09ea614 100644 --- a/include/utils/Functor.h +++ b/include/utils/Functor.h @@ -25,7 +25,7 @@ class Functor { public: Functor() {} virtual ~Functor() {} - virtual status_t operator ()(int what, void* data) { return NO_ERROR; } + virtual status_t operator ()(int /*what*/, void* /*data*/) { return NO_ERROR; } }; }; // namespace android diff --git a/include/utils/LruCache.h b/include/utils/LruCache.h index 053bfaf..9248ac9 100644 --- a/include/utils/LruCache.h +++ b/include/utils/LruCache.h @@ -56,7 +56,7 @@ public: bool next() { mIndex = mCache.mTable->next(mIndex); - return mIndex != -1; + return (ssize_t)mIndex != -1; } size_t index() const { @@ -103,9 +103,13 @@ private: // Implementation is here, because it's fully templated template <typename TKey, typename TValue> -LruCache<TKey, TValue>::LruCache(uint32_t maxCapacity): mMaxCapacity(maxCapacity), - mNullValue(NULL), mTable(new BasicHashtable<TKey, Entry>), mYoungest(NULL), mOldest(NULL), - mListener(NULL) { +LruCache<TKey, TValue>::LruCache(uint32_t maxCapacity) + : mTable(new BasicHashtable<TKey, Entry>) + , mListener(NULL) + , mOldest(NULL) + , mYoungest(NULL) + , mMaxCapacity(maxCapacity) + , mNullValue(NULL) { }; template<typename K, typename V> diff --git a/include/utils/Unicode.h b/include/utils/Unicode.h index c8c87c3..5b98de2 100644 --- a/include/utils/Unicode.h +++ b/include/utils/Unicode.h @@ -22,8 +22,11 @@ extern "C" { +// Definitions exist in C++11 +#if defined __cplusplus && __cplusplus < 201103L typedef uint32_t char32_t; typedef uint16_t char16_t; +#endif // Standard string functions on char16_t strings. int strcmp16(const char16_t *, const char16_t *); diff --git a/init/Android.mk b/init/Android.mk index 1f43ba6..489dc93 100644 --- a/init/Android.mk +++ b/init/Android.mk @@ -17,15 +17,20 @@ LOCAL_SRC_FILES:= \ ueventd_parser.c \ watchdogd.c +LOCAL_CFLAGS += -Wno-unused-parameter + ifeq ($(strip $(INIT_BOOTCHART)),true) LOCAL_SRC_FILES += bootchart.c LOCAL_CFLAGS += -DBOOTCHART=1 endif ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT))) -LOCAL_CFLAGS += -DALLOW_LOCAL_PROP_OVERRIDE=1 +LOCAL_CFLAGS += -DALLOW_LOCAL_PROP_OVERRIDE=1 -DALLOW_DISABLE_SELINUX=1 endif +# Enable ueventd logging +#LOCAL_CFLAGS += -DLOG_UEVENTS=1 + LOCAL_MODULE:= init LOCAL_FORCE_STATIC_EXECUTABLE := true @@ -42,6 +47,8 @@ LOCAL_STATIC_LIBRARIES := \ libmincrypt \ libext4_utils_static +LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk + include $(BUILD_EXECUTABLE) # Make a symlink from /sbin/ueventd and /sbin/watchdogd to /init diff --git a/init/builtins.c b/init/builtins.c index e2932d5..b32981e 100644 --- a/init/builtins.c +++ b/init/builtins.c @@ -33,7 +33,6 @@ #include <linux/loop.h> #include <cutils/partition_utils.h> #include <cutils/android_reboot.h> -#include <sys/system_properties.h> #include <fs_mgr.h> #include <selinux/selinux.h> @@ -196,6 +195,8 @@ static void service_start_if_not_disabled(struct service *svc) { if (!(svc->flags & SVC_DISABLED)) { service_start(svc, NULL); + } else { + svc->flags |= SVC_DISABLED_START; } } @@ -238,6 +239,21 @@ int do_domainname(int nargs, char **args) return write_file("/proc/sys/kernel/domainname", args[1]); } +int do_enable(int nargs, char **args) +{ + struct service *svc; + svc = service_find_by_name(args[1]); + if (svc) { + svc->flags &= ~(SVC_DISABLED | SVC_RC_DISABLED); + if (svc->flags & SVC_DISABLED_START) { + service_start(svc, NULL); + } + } else { + return -1; + } + return 0; +} + int do_exec(int nargs, char **args) { return -1; @@ -846,11 +862,24 @@ int do_setsebool(int nargs, char **args) { } int do_loglevel(int nargs, char **args) { - if (nargs == 2) { - klog_set_level(atoi(args[1])); - return 0; + int log_level; + char log_level_str[PROP_VALUE_MAX] = ""; + if (nargs != 2) { + ERROR("loglevel: missing argument\n"); + return -EINVAL; } - return -1; + + if (expand_props(log_level_str, args[1], sizeof(log_level_str))) { + ERROR("loglevel: cannot expand '%s'\n", args[1]); + return -EINVAL; + } + log_level = atoi(log_level_str); + if (log_level < KLOG_ERROR_LEVEL || log_level > KLOG_DEBUG_LEVEL) { + ERROR("loglevel: invalid log level'%d'\n", log_level); + return -EINVAL; + } + klog_set_level(log_level); + return 0; } int do_load_persist_props(int nargs, char **args) { @@ -861,6 +890,14 @@ int do_load_persist_props(int nargs, char **args) { return -1; } +int do_load_all_props(int nargs, char **args) { + if (nargs == 1) { + load_all_props(); + return 0; + } + return -1; +} + int do_wait(int nargs, char **args) { if (nargs == 2) { diff --git a/init/devices.c b/init/devices.c index f7df453..e27c311 100644 --- a/init/devices.c +++ b/init/devices.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 The Android Open Source Project + * Copyright (C) 2007-2014 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. @@ -15,6 +15,7 @@ */ #include <errno.h> +#include <fnmatch.h> #include <stddef.h> #include <stdio.h> #include <stdlib.h> @@ -37,7 +38,6 @@ #include <private/android_filesystem_config.h> #include <sys/time.h> -#include <asm/page.h> #include <sys/wait.h> #include <cutils/list.h> @@ -48,6 +48,8 @@ #include "util.h" #include "log.h" +#define UNUSED __attribute__((__unused__)) + #define SYSFS_PREFIX "/sys" #define FIRMWARE_DIR1 "/etc/firmware" #define FIRMWARE_DIR2 "/vendor/firmware" @@ -76,6 +78,7 @@ struct perms_ { unsigned int uid; unsigned int gid; unsigned short prefix; + unsigned short wildcard; }; struct perm_node { @@ -96,7 +99,8 @@ static list_declare(platform_names); int add_dev_perms(const char *name, const char *attr, mode_t perm, unsigned int uid, unsigned int gid, - unsigned short prefix) { + unsigned short prefix, + unsigned short wildcard) { struct perm_node *node = calloc(1, sizeof(*node)); if (!node) return -ENOMEM; @@ -115,6 +119,7 @@ int add_dev_perms(const char *name, const char *attr, node->dp.uid = uid; node->dp.gid = gid; node->dp.prefix = prefix; + node->dp.wildcard = wildcard; if (attr) list_add_tail(&sys_perms, &node->plist); @@ -129,40 +134,62 @@ void fixup_sys_perms(const char *upath) char buf[512]; struct listnode *node; struct perms_ *dp; - char *secontext; - /* upaths omit the "/sys" that paths in this list - * contain, so we add 4 when comparing... - */ + /* upaths omit the "/sys" that paths in this list + * contain, so we add 4 when comparing... + */ list_for_each(node, &sys_perms) { dp = &(node_to_item(node, struct perm_node, plist))->dp; if (dp->prefix) { if (strncmp(upath, dp->name + 4, strlen(dp->name + 4))) continue; + } else if (dp->wildcard) { + if (fnmatch(dp->name + 4, upath, FNM_PATHNAME) != 0) + continue; } else { if (strcmp(upath, dp->name + 4)) continue; } if ((strlen(upath) + strlen(dp->attr) + 6) > sizeof(buf)) - return; + break; sprintf(buf,"/sys%s/%s", upath, dp->attr); INFO("fixup %s %d %d 0%o\n", buf, dp->uid, dp->gid, dp->perm); chown(buf, dp->uid, dp->gid); chmod(buf, dp->perm); - if (sehandle) { - secontext = NULL; - selabel_lookup(sehandle, &secontext, buf, 0); - if (secontext) { - setfilecon(buf, secontext); - freecon(secontext); - } - } } + + // Now fixup SELinux file labels + int len = snprintf(buf, sizeof(buf), "/sys%s", upath); + if ((len < 0) || ((size_t) len >= sizeof(buf))) { + // Overflow + return; + } + if (access(buf, F_OK) == 0) { + INFO("restorecon_recursive: %s\n", buf); + restorecon_recursive(buf); + } +} + +static bool perm_path_matches(const char *path, struct perms_ *dp) +{ + if (dp->prefix) { + if (strncmp(path, dp->name, strlen(dp->name)) == 0) + return true; + } else if (dp->wildcard) { + if (fnmatch(dp->name, path, FNM_PATHNAME) == 0) + return true; + } else { + if (strcmp(path, dp->name) == 0) + return true; + } + + return false; } -static mode_t get_device_perm(const char *path, unsigned *uid, unsigned *gid) +static mode_t get_device_perm(const char *path, const char **links, + unsigned *uid, unsigned *gid) { mode_t perm; struct listnode *node; @@ -173,19 +200,30 @@ static mode_t get_device_perm(const char *path, unsigned *uid, unsigned *gid) * override ueventd.rc */ list_for_each_reverse(node, &dev_perms) { + bool match = false; + perm_node = node_to_item(node, struct perm_node, plist); dp = &perm_node->dp; - if (dp->prefix) { - if (strncmp(path, dp->name, strlen(dp->name))) - continue; + if (perm_path_matches(path, dp)) { + match = true; } else { - if (strcmp(path, dp->name)) - continue; + if (links) { + int i; + for (i = 0; links[i]; i++) { + if (perm_path_matches(links[i], dp)) { + match = true; + break; + } + } + } + } + + if (match) { + *uid = dp->uid; + *gid = dp->gid; + return dp->perm; } - *uid = dp->uid; - *gid = dp->gid; - return dp->perm; } /* Default if nothing found. */ *uid = 0; @@ -194,8 +232,9 @@ static mode_t get_device_perm(const char *path, unsigned *uid, unsigned *gid) } static void make_device(const char *path, - const char *upath, - int block, int major, int minor) + const char *upath UNUSED, + int block, int major, int minor, + const char **links) { unsigned uid; unsigned gid; @@ -203,10 +242,10 @@ static void make_device(const char *path, dev_t dev; char *secontext = NULL; - mode = get_device_perm(path, &uid, &gid) | (block ? S_IFBLK : S_IFCHR); + mode = get_device_perm(path, links, &uid, &gid) | (block ? S_IFBLK : S_IFCHR); if (sehandle) { - selabel_lookup(sehandle, &secontext, path, mode); + selabel_lookup_best_match(sehandle, &secontext, path, links, mode); setfscreatecon(secontext); } @@ -297,6 +336,37 @@ static void remove_platform_device(const char *path) } } +/* Given a path that may start with a PCI device, populate the supplied buffer + * with the PCI domain/bus number and the peripheral ID and return 0. + * If it doesn't start with a PCI device, or there is some error, return -1 */ +static int find_pci_device_prefix(const char *path, char *buf, ssize_t buf_sz) +{ + const char *start, *end; + + if (strncmp(path, "/devices/pci", 12)) + return -1; + + /* Beginning of the prefix is the initial "pci" after "/devices/" */ + start = path + 9; + + /* End of the prefix is two path '/' later, capturing the domain/bus number + * and the peripheral ID. Example: pci0000:00/0000:00:1f.2 */ + end = strchr(start, '/'); + if (!end) + return -1; + end = strchr(end + 1, '/'); + if (!end) + return -1; + + /* Make sure we have enough room for the string plus null terminator */ + if (end - start + 1 > buf_sz) + return -1; + + strncpy(buf, start, end - start); + buf[end - start] = '\0'; + return 0; +} + #if LOG_UEVENTS static inline suseconds_t get_usecs(void) @@ -421,11 +491,12 @@ err: return NULL; } -static char **parse_platform_block_device(struct uevent *uevent) +static char **get_block_device_symlinks(struct uevent *uevent) { const char *device; struct platform_node *pdev; char *slash; + const char *type; int width; char buf[256]; char link_path[256]; @@ -437,18 +508,24 @@ static char **parse_platform_block_device(struct uevent *uevent) struct stat info; pdev = find_platform_device(uevent->path); - if (!pdev) + if (pdev) { + device = pdev->name; + type = "platform"; + } else if (!find_pci_device_prefix(uevent->path, buf, sizeof(buf))) { + device = buf; + type = "pci"; + } else { return NULL; - device = pdev->name; + } char **links = malloc(sizeof(char *) * 4); if (!links) return NULL; memset(links, 0, sizeof(char *) * 4); - INFO("found platform device %s\n", device); + INFO("found %s device %s\n", type, device); - snprintf(link_path, sizeof(link_path), "/dev/block/platform/%s", device); + snprintf(link_path, sizeof(link_path), "/dev/block/%s/%s", type, device); if (uevent->partition_name) { p = strdup(uevent->partition_name); @@ -484,7 +561,7 @@ static void handle_device(const char *action, const char *devpath, int i; if(!strcmp(action, "add")) { - make_device(devpath, path, block, major, minor); + make_device(devpath, path, block, major, minor, (const char **)links); if (links) { for (i = 0; links[i]; i++) make_link(devpath, links[i]); @@ -555,7 +632,7 @@ static void handle_block_device_event(struct uevent *uevent) make_dir(base, 0755); if (!strncmp(uevent->path, "/devices/", 9)) - links = parse_platform_block_device(uevent); + links = get_block_device_symlinks(uevent); handle_device(uevent->action, devpath, uevent->path, 1, uevent->major, uevent->minor, links); @@ -590,6 +667,11 @@ static void mkdir_recursive_for_devpath(const char *devpath) mkdir_recursive(dir, 0755); } +static inline void __attribute__((__deprecated__)) kernel_logger() +{ + INFO("kernel logger is deprecated\n"); +} + static void handle_generic_device_event(struct uevent *uevent) { char *base; @@ -676,6 +758,7 @@ static void handle_generic_device_event(struct uevent *uevent) make_dir(base, 0755); } else if(!strncmp(uevent->subsystem, "misc", 4) && !strncmp(name, "log_", 4)) { + kernel_logger(); base = "/dev/log/"; make_dir(base, 0755); name += 4; @@ -692,7 +775,7 @@ static void handle_generic_device_event(struct uevent *uevent) static void handle_device_event(struct uevent *uevent) { - if (!strcmp(uevent->action,"add") || !strcmp(uevent->action, "change")) + if (!strcmp(uevent->action,"add") || !strcmp(uevent->action, "change") || !strcmp(uevent->action, "online")) fixup_sys_perms(uevent->path); if (!strncmp(uevent->subsystem, "block", 5)) { diff --git a/init/devices.h b/init/devices.h index a84fa58..5d0fe88 100644 --- a/init/devices.h +++ b/init/devices.h @@ -23,6 +23,7 @@ extern void handle_device_fd(); extern void device_init(void); extern int add_dev_perms(const char *name, const char *attr, mode_t perm, unsigned int uid, - unsigned int gid, unsigned short prefix); + unsigned int gid, unsigned short prefix, + unsigned short wildcard); int get_device_fd(); #endif /* _INIT_DEVICES_H */ diff --git a/init/init.c b/init/init.c index 00f4558..e4ac1cf 100644 --- a/init/init.c +++ b/init/init.c @@ -46,8 +46,6 @@ #include <private/android_filesystem_config.h> #include <termios.h> -#include <sys/system_properties.h> - #include "devices.h" #include "init.h" #include "log.h" @@ -164,7 +162,7 @@ void service_start(struct service *svc, const char *dynamic_args) * state and immediately takes it out of the restarting * state if it was in there */ - svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART)); + svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START)); svc->time_started = 0; /* running processes require no additional work -- if @@ -364,7 +362,7 @@ static void service_stop_or_reset(struct service *svc, int how) { /* The service is still SVC_RUNNING until its process exits, but if it has * already exited it shoudn't attempt a restart yet. */ - svc->flags &= (~SVC_RESTARTING); + svc->flags &= ~(SVC_RESTARTING | SVC_DISABLED_START); if ((how != SVC_DISABLED) && (how != SVC_RESET) && (how != SVC_RESTART)) { /* Hrm, an illegal flag. Default to SVC_DISABLED */ @@ -530,7 +528,8 @@ static int is_last_command(struct action *act, struct command *cmd) void execute_one_command(void) { - int ret; + int ret, i; + char cmd_str[256] = ""; if (!cur_action || !cur_command || is_last_command(cur_action, cur_command)) { cur_action = action_remove_queue_head(); @@ -547,7 +546,17 @@ void execute_one_command(void) return; ret = cur_command->func(cur_command->nargs, cur_command->args); - INFO("command '%s' r=%d\n", cur_command->args[0], ret); + if (klog_get_level() >= KLOG_INFO_LEVEL) { + for (i = 0; i < cur_command->nargs; i++) { + strlcat(cmd_str, cur_command->args[i], sizeof(cmd_str)); + if (i < cur_command->nargs - 1) { + strlcat(cmd_str, " ", sizeof(cmd_str)); + } + } + INFO("command '%s' action=%s status=%d (%s:%d)\n", + cmd_str, cur_action ? cur_action->name : "", ret, cur_command->filename, + cur_command->line); + } } static int wait_for_coldboot_done_action(int nargs, char **args) @@ -843,24 +852,21 @@ static int bootchart_init_action(int nargs, char **args) static const struct selinux_opt seopts_prop[] = { { SELABEL_OPT_PATH, "/property_contexts" }, + { SELABEL_OPT_PATH, "/data/security/current/property_contexts" }, { 0, NULL } }; struct selabel_handle* selinux_android_prop_context_handle(void) { - int i = 0; - struct selabel_handle* sehandle = NULL; - while ((sehandle == NULL) && seopts_prop[i].value) { - sehandle = selabel_open(SELABEL_CTX_ANDROID_PROP, &seopts_prop[i], 1); - i++; - } - + int policy_index = selinux_android_use_data_policy() ? 1 : 0; + struct selabel_handle* sehandle = selabel_open(SELABEL_CTX_ANDROID_PROP, + &seopts_prop[policy_index], 1); if (!sehandle) { ERROR("SELinux: Could not load property_contexts: %s\n", strerror(errno)); return NULL; } - INFO("SELinux: Loaded property contexts from %s\n", seopts_prop[i - 1].value); + INFO("SELinux: Loaded property contexts from %s\n", seopts_prop[policy_index].value); return sehandle; } @@ -873,6 +879,7 @@ void selinux_init_all_handles(void) static bool selinux_is_disabled(void) { +#ifdef ALLOW_DISABLE_SELINUX char tmp[PROP_VALUE_MAX]; if (access("/sys/fs/selinux", F_OK) != 0) { @@ -886,12 +893,14 @@ static bool selinux_is_disabled(void) /* SELinux is compiled into the kernel, but we've been told to disable it. */ return true; } +#endif return false; } static bool selinux_is_enforcing(void) { +#ifdef ALLOW_DISABLE_SELINUX char tmp[PROP_VALUE_MAX]; if (property_get("ro.boot.selinux", tmp) == 0) { @@ -908,6 +917,7 @@ static bool selinux_is_enforcing(void) ERROR("SELinux: Unknown value of ro.boot.selinux. Got: \"%s\". Assuming enforcing.\n", tmp); } +#endif return true; } @@ -933,12 +943,33 @@ int selinux_reload_policy(void) return 0; } -int audit_callback(void *data, security_class_t cls, char *buf, size_t len) +static int audit_callback(void *data, security_class_t cls __attribute__((unused)), char *buf, size_t len) { snprintf(buf, len, "property=%s", !data ? "NULL" : (char *)data); return 0; } +int log_callback(int type, const char *fmt, ...) +{ + int level; + va_list ap; + switch (type) { + case SELINUX_WARNING: + level = KLOG_WARNING_LEVEL; + break; + case SELINUX_INFO: + level = KLOG_INFO_LEVEL; + break; + default: + level = KLOG_ERROR_LEVEL; + break; + } + va_start(ap, fmt); + klog_vwrite(level, fmt, ap); + va_end(ap); + return 0; +} + static void selinux_initialize(void) { if (selinux_is_disabled()) { @@ -1012,7 +1043,7 @@ int main(int argc, char **argv) process_kernel_cmdline(); union selinux_callback cb; - cb.func_log = klog_write; + cb.func_log = log_callback; selinux_set_callback(SELINUX_CB_LOG, cb); cb.func_audit = audit_callback; @@ -1031,8 +1062,7 @@ int main(int argc, char **argv) is_charger = !strcmp(bootmode, "charger"); INFO("property init\n"); - if (!is_charger) - property_load_boot_defaults(); + property_load_boot_defaults(); INFO("reading config file\n"); init_parse_config_file("/init.rc"); @@ -1047,28 +1077,19 @@ int main(int argc, char **argv) /* execute all the boot actions to get us started */ action_for_each_trigger("init", action_add_queue_tail); - /* skip mounting filesystems in charger mode */ - if (!is_charger) { - action_for_each_trigger("early-fs", action_add_queue_tail); - action_for_each_trigger("fs", action_add_queue_tail); - action_for_each_trigger("post-fs", action_add_queue_tail); - action_for_each_trigger("post-fs-data", action_add_queue_tail); - } - /* Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random * wasn't ready immediately after wait_for_coldboot_done */ queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng"); - queue_builtin_action(property_service_init_action, "property_service_init"); queue_builtin_action(signal_init_action, "signal_init"); queue_builtin_action(check_startup_action, "check_startup"); + /* Don't mount filesystems or start core system services if in charger mode. */ if (is_charger) { action_for_each_trigger("charger", action_add_queue_tail); } else { - action_for_each_trigger("early-boot", action_add_queue_tail); - action_for_each_trigger("boot", action_add_queue_tail); + action_for_each_trigger("late-init", action_add_queue_tail); } /* run all property triggers based on current state of the properties */ diff --git a/init/init.h b/init/init.h index 736b75b..a7615a3 100644 --- a/init/init.h +++ b/init/init.h @@ -29,10 +29,14 @@ struct command struct listnode clist; int (*func)(int nargs, char **args); + + int line; + const char *filename; + int nargs; char *args[1]; }; - + struct action { /* node in list of all actions */ struct listnode alist; @@ -43,7 +47,7 @@ struct action { unsigned hash; const char *name; - + struct listnode commands; struct command *current; }; @@ -74,6 +78,7 @@ struct svcenvinfo { so it can be restarted with its class */ #define SVC_RC_DISABLED 0x80 /* Remember if the disabled flag was set in the rc script */ #define SVC_RESTART 0x100 /* Use to safely restart (stop, wait, start) a service */ +#define SVC_DISABLED_START 0x200 /* a start was requested but it was disabled at the time */ #define NR_SVC_SUPP_GIDS 12 /* twelve supplementary groups */ diff --git a/init/init_parser.c b/init/init_parser.c index f49e698..6466db2 100644 --- a/init/init_parser.c +++ b/init/init_parser.c @@ -33,9 +33,6 @@ #include <cutils/iosched_policy.h> #include <cutils/list.h> -#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_ -#include <sys/_system_properties.h> - static list_declare(service_list); static list_declare(action_list); static list_declare(action_queue); @@ -98,6 +95,7 @@ static int lookup_keyword(const char *s) if (!strcmp(s, "omainname")) return K_domainname; break; case 'e': + if (!strcmp(s, "nable")) return K_enable; if (!strcmp(s, "xec")) return K_exec; if (!strcmp(s, "xport")) return K_export; break; @@ -119,6 +117,7 @@ static int lookup_keyword(const char *s) case 'l': if (!strcmp(s, "oglevel")) return K_loglevel; if (!strcmp(s, "oad_persist_props")) return K_load_persist_props; + if (!strcmp(s, "oad_all_props")) return K_load_all_props; break; case 'm': if (!strcmp(s, "kdir")) return K_mkdir; @@ -582,6 +581,7 @@ void queue_builtin_action(int (*func)(int nargs, char **args), char *name) cmd = calloc(1, sizeof(*cmd)); cmd->func = func; cmd->args[0] = name; + cmd->nargs = 1; list_add_tail(&act->commands, &cmd->clist); list_add_tail(&action_list, &act->alist); @@ -759,7 +759,7 @@ static void parse_line_service(struct parse_state *state, int nargs, char **args break; case K_setenv: { /* name value */ struct svcenvinfo *ei; - if (nargs < 2) { + if (nargs < 3) { parse_error(state, "setenv option requires name and value arguments\n"); break; } @@ -868,6 +868,8 @@ static void parse_line_action(struct parse_state* state, int nargs, char **args) } cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs); cmd->func = kw_func(kw); + cmd->line = state->line; + cmd->filename = state->filename; cmd->nargs = nargs; memcpy(cmd->args, args, sizeof(char*) * nargs); list_add_tail(&act->commands, &cmd->clist); diff --git a/init/keywords.h b/init/keywords.h index 97fe50c..2d97e5b 100644 --- a/init/keywords.h +++ b/init/keywords.h @@ -6,6 +6,7 @@ int do_class_start(int nargs, char **args); int do_class_stop(int nargs, char **args); int do_class_reset(int nargs, char **args); int do_domainname(int nargs, char **args); +int do_enable(int nargs, char **args); int do_exec(int nargs, char **args); int do_export(int nargs, char **args); int do_hostname(int nargs, char **args); @@ -38,6 +39,7 @@ int do_chown(int nargs, char **args); int do_chmod(int nargs, char **args); int do_loglevel(int nargs, char **args); int do_load_persist_props(int nargs, char **args); +int do_load_all_props(int nargs, char **args); int do_wait(int nargs, char **args); #define __MAKE_KEYWORD_ENUM__ #define KEYWORD(symbol, flags, nargs, func) K_##symbol, @@ -55,6 +57,7 @@ enum { KEYWORD(critical, OPTION, 0, 0) KEYWORD(disabled, OPTION, 0, 0) KEYWORD(domainname, COMMAND, 1, do_domainname) + KEYWORD(enable, COMMAND, 1, do_enable) KEYWORD(exec, COMMAND, 1, do_exec) KEYWORD(export, COMMAND, 2, do_export) KEYWORD(group, OPTION, 0, 0) @@ -99,6 +102,7 @@ enum { KEYWORD(chmod, COMMAND, 2, do_chmod) KEYWORD(loglevel, COMMAND, 1, do_loglevel) KEYWORD(load_persist_props, COMMAND, 0, do_load_persist_props) + KEYWORD(load_all_props, COMMAND, 0, do_load_all_props) KEYWORD(ioprio, OPTION, 0, 0) #ifdef __MAKE_KEYWORD_ENUM__ KEYWORD_COUNT, @@ -23,6 +23,6 @@ #define NOTICE(x...) KLOG_NOTICE("init", x) #define INFO(x...) KLOG_INFO("init", x) -#define LOG_UEVENTS 0 /* log uevent messages if 1. verbose */ +extern int log_callback(int type, const char *fmt, ...); #endif diff --git a/init/property_service.c b/init/property_service.c index ac63377..d112699 100644 --- a/init/property_service.c +++ b/init/property_service.c @@ -38,7 +38,6 @@ #include <sys/types.h> #include <netinet/in.h> #include <sys/mman.h> -#include <sys/atomics.h> #include <private/android_filesystem_config.h> #include <selinux/selinux.h> @@ -56,63 +55,6 @@ static int property_area_inited = 0; static int property_set_fd = -1; -/* White list of permissions for setting property services. */ -struct { - const char *prefix; - unsigned int uid; - unsigned int gid; -} property_perms[] = { - { "net.rmnet0.", AID_RADIO, 0 }, - { "net.gprs.", AID_RADIO, 0 }, - { "net.ppp", AID_RADIO, 0 }, - { "net.qmi", AID_RADIO, 0 }, - { "net.lte", AID_RADIO, 0 }, - { "net.cdma", AID_RADIO, 0 }, - { "ril.", AID_RADIO, 0 }, - { "gsm.", AID_RADIO, 0 }, - { "persist.radio", AID_RADIO, 0 }, - { "net.dns", AID_RADIO, 0 }, - { "sys.usb.config", AID_RADIO, 0 }, - { "net.", AID_SYSTEM, 0 }, - { "dev.", AID_SYSTEM, 0 }, - { "runtime.", AID_SYSTEM, 0 }, - { "hw.", AID_SYSTEM, 0 }, - { "sys.", AID_SYSTEM, 0 }, - { "sys.powerctl", AID_SHELL, 0 }, - { "service.", AID_SYSTEM, 0 }, - { "wlan.", AID_SYSTEM, 0 }, - { "gps.", AID_GPS, 0 }, - { "bluetooth.", AID_BLUETOOTH, 0 }, - { "dhcp.", AID_SYSTEM, 0 }, - { "dhcp.", AID_DHCP, 0 }, - { "debug.", AID_SYSTEM, 0 }, - { "debug.", AID_SHELL, 0 }, - { "log.", AID_SHELL, 0 }, - { "service.adb.root", AID_SHELL, 0 }, - { "service.adb.tcp.port", AID_SHELL, 0 }, - { "persist.sys.", AID_SYSTEM, 0 }, - { "persist.service.", AID_SYSTEM, 0 }, - { "persist.security.", AID_SYSTEM, 0 }, - { "persist.gps.", AID_GPS, 0 }, - { "persist.service.bdroid.", AID_BLUETOOTH, 0 }, - { "selinux." , AID_SYSTEM, 0 }, - { NULL, 0, 0 } -}; - -/* - * White list of UID that are allowed to start/stop services. - * Currently there are no user apps that require. - */ -struct { - const char *service; - unsigned int uid; - unsigned int gid; -} control_perms[] = { - { "dumpstate",AID_SHELL, AID_LOG }, - { "ril-daemon",AID_RADIO, AID_RADIO }, - {NULL, 0, 0 } -}; - typedef struct { size_t size; int fd; @@ -194,34 +136,10 @@ static int check_control_mac_perms(const char *name, char *sctx) } /* - * Checks permissions for starting/stoping system services. - * AID_SYSTEM and AID_ROOT are always allowed. - * - * Returns 1 if uid allowed, 0 otherwise. - */ -static int check_control_perms(const char *name, unsigned int uid, unsigned int gid, char *sctx) { - - int i; - if (uid == AID_SYSTEM || uid == AID_ROOT) - return check_control_mac_perms(name, sctx); - - /* Search the ACL */ - for (i = 0; control_perms[i].service; i++) { - if (strcmp(control_perms[i].service, name) == 0) { - if ((uid && control_perms[i].uid == uid) || - (gid && control_perms[i].gid == gid)) { - return check_control_mac_perms(name, sctx); - } - } - } - return 0; -} - -/* * Checks permissions for setting system properties. * Returns 1 if uid allowed, 0 otherwise. */ -static int check_perms(const char *name, unsigned int uid, unsigned int gid, char *sctx) +static int check_perms(const char *name, char *sctx) { int i; unsigned int app_id; @@ -229,26 +147,7 @@ static int check_perms(const char *name, unsigned int uid, unsigned int gid, cha if(!strncmp(name, "ro.", 3)) name +=3; - if (uid == 0) - return check_mac_perms(name, sctx); - - app_id = multiuser_get_app_id(uid); - if (app_id == AID_BLUETOOTH) { - uid = app_id; - } - - for (i = 0; property_perms[i].prefix; i++) { - if (strncmp(property_perms[i].prefix, name, - strlen(property_perms[i].prefix)) == 0) { - if ((uid && property_perms[i].uid == uid) || - (gid && property_perms[i].gid == gid)) { - - return check_mac_perms(name, sctx); - } - } - } - - return 0; + return check_mac_perms(name, sctx); } int __property_get(const char *name, char *value) @@ -269,6 +168,7 @@ static void write_persistent_property(const char *name, const char *value) return; } write(fd, value, strlen(value)); + fsync(fd); close(fd); snprintf(path, sizeof(path), "%s/%s", PERSISTENT_PROPERTY_DIR, name); @@ -405,14 +305,14 @@ void handle_property_set_fd() // Keep the old close-socket-early behavior when handling // ctl.* properties. close(s); - if (check_control_perms(msg.value, cr.uid, cr.gid, source_ctx)) { + if (check_control_mac_perms(msg.value, source_ctx)) { handle_control_message((char*) msg.name + 4, (char*) msg.value); } else { ERROR("sys_prop: Unable to %s service ctl [%s] uid:%d gid:%d pid:%d\n", msg.name + 4, msg.value, cr.uid, cr.gid, cr.pid); } } else { - if (check_perms(msg.name, cr.uid, cr.gid, source_ctx)) { + if (check_perms(msg.name, source_ctx)) { property_set((char*) msg.name, (char*) msg.value); } else { ERROR("sys_prop: permission denied uid:%d name:%s\n", @@ -439,40 +339,73 @@ void get_property_workspace(int *fd, int *sz) *sz = pa_workspace.size; } -static void load_properties(char *data, char *prefix) +static void load_properties_from_file(const char *, const char *); + +/* + * Filter is used to decide which properties to load: NULL loads all keys, + * "ro.foo.*" is a prefix match, and "ro.foo.bar" is an exact match. + */ +static void load_properties(char *data, const char *filter) { - char *key, *value, *eol, *sol, *tmp; - size_t plen; + char *key, *value, *eol, *sol, *tmp, *fn; + size_t flen = 0; + + if (filter) { + flen = strlen(filter); + } - if (prefix) - plen = strlen(prefix); sol = data; - while((eol = strchr(sol, '\n'))) { + while ((eol = strchr(sol, '\n'))) { key = sol; *eol++ = 0; sol = eol; - value = strchr(key, '='); - if(value == 0) continue; - *value++ = 0; + while (isspace(*key)) key++; + if (*key == '#') continue; - while(isspace(*key)) key++; - if(*key == '#') continue; - tmp = value - 2; - while((tmp > key) && isspace(*tmp)) *tmp-- = 0; + tmp = eol - 2; + while ((tmp > key) && isspace(*tmp)) *tmp-- = 0; - if (prefix && strncmp(key, prefix, plen)) - continue; + if (!strncmp(key, "import ", 7) && flen == 0) { + fn = key + 7; + while (isspace(*fn)) fn++; - while(isspace(*value)) value++; - tmp = eol - 2; - while((tmp > value) && isspace(*tmp)) *tmp-- = 0; + key = strchr(fn, ' '); + if (key) { + *key++ = 0; + while (isspace(*key)) key++; + } + + load_properties_from_file(fn, key); + + } else { + value = strchr(key, '='); + if (!value) continue; + *value++ = 0; + + tmp = value - 2; + while ((tmp > key) && isspace(*tmp)) *tmp-- = 0; + + while (isspace(*value)) value++; + + if (flen > 0) { + if (filter[flen - 1] == '*') { + if (strncmp(key, filter, flen - 1)) continue; + } else { + if (strcmp(key, filter)) continue; + } + } - property_set(key, value); + property_set(key, value); + } } } -static void load_properties_from_file(const char *fn, char *prefix) +/* + * Filter is used to decide which properties to load: NULL loads all keys, + * "ro.foo.*" is a prefix match, and "ro.foo.bar" is an exact match. + */ +static void load_properties_from_file(const char *fn, const char *filter) { char *data; unsigned sz; @@ -480,7 +413,7 @@ static void load_properties_from_file(const char *fn, char *prefix) data = read_file(fn, &sz); if(data != 0) { - load_properties(data, prefix); + load_properties(data, filter); free(data); } } @@ -523,7 +456,8 @@ static void load_persistent_properties() || (sb.st_gid != 0) || (sb.st_nlink != 1)) { ERROR("skipping insecure property file %s (uid=%u gid=%u nlink=%d mode=%o)\n", - entry->d_name, sb.st_uid, sb.st_gid, sb.st_nlink, sb.st_mode); + entry->d_name, (unsigned int)sb.st_uid, (unsigned int)sb.st_gid, + sb.st_nlink, sb.st_mode); close(fd); continue; } @@ -586,16 +520,21 @@ void load_persist_props(void) load_persistent_properties(); } -void start_property_service(void) +void load_all_props(void) { - int fd; - load_properties_from_file(PROP_PATH_SYSTEM_BUILD, NULL); load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT, NULL); - load_properties_from_file(PROP_PATH_FACTORY, "ro."); + load_properties_from_file(PROP_PATH_FACTORY, "ro.*"); + load_override_properties(); + /* Read persistent properties after all default values have been loaded. */ load_persistent_properties(); +} + +void start_property_service(void) +{ + int fd; fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM, 0666, 0, 0, NULL); if(fd < 0) return; diff --git a/init/property_service.h b/init/property_service.h index 46cbd8f..730495e 100644 --- a/init/property_service.h +++ b/init/property_service.h @@ -24,6 +24,7 @@ extern void handle_property_set_fd(void); extern void property_init(void); extern void property_load_boot_defaults(void); extern void load_persist_props(void); +extern void load_all_props(void); extern void start_property_service(void); void get_property_workspace(int *fd, int *sz); extern int __property_get(const char *name, char *value); diff --git a/init/readme.txt b/init/readme.txt index 42a09cb..613a9e9 100644 --- a/init/readme.txt +++ b/init/readme.txt @@ -178,6 +178,16 @@ class_stop <serviceclass> domainname <name> Set the domain name. +enable <servicename> + Turns a disabled service into an enabled one as if the service did not + specify disabled. + If the service is supposed to be running, it will be started now. + Typically used when the bootloader sets a variable that indicates a specific + service should be started when needed. E.g. + on property:ro.boot.myfancyhardware=1 + enable my_fancy_service_for_my_fancy_hardware + + insmod <path> Install the module at <path> diff --git a/init/ueventd.c b/init/ueventd.c index 3d01836..833e4fd 100644 --- a/init/ueventd.c +++ b/init/ueventd.c @@ -21,6 +21,7 @@ #include <stdio.h> #include <ctype.h> #include <signal.h> +#include <selinux/selinux.h> #include <private/android_filesystem_config.h> @@ -69,6 +70,16 @@ int ueventd_main(int argc, char **argv) open_devnull_stdio(); klog_init(); +#if LOG_UEVENTS + /* Ensure we're at a logging level that will show the events */ + if (klog_get_level() < KLOG_INFO_LEVEL) { + klog_set_level(KLOG_INFO_LEVEL); + } +#endif + + union selinux_callback cb; + cb.func_log = log_callback; + selinux_set_callback(SELINUX_CB_LOG, cb); INFO("starting ueventd\n"); @@ -116,6 +127,7 @@ void set_device_permission(int nargs, char **args) uid_t uid; gid_t gid; int prefix = 0; + int wildcard = 0; char *endptr; int ret; char *tmp = 0; @@ -148,9 +160,13 @@ void set_device_permission(int nargs, char **args) name = tmp; } else { int len = strlen(name); - if (name[len - 1] == '*') { + char *wildcard_chr = strchr(name, '*'); + if ((name[len - 1] == '*') && + (wildcard_chr == (name + len - 1))) { prefix = 1; name[len - 1] = '\0'; + } else if (wildcard_chr) { + wildcard = 1; } } @@ -177,6 +193,6 @@ void set_device_permission(int nargs, char **args) } gid = ret; - add_dev_perms(name, attr, perm, uid, gid, prefix); + add_dev_perms(name, attr, perm, uid, gid, prefix, wildcard); free(tmp); } diff --git a/init/util.c b/init/util.c index e772342..0f69e1c 100644 --- a/init/util.c +++ b/init/util.c @@ -527,10 +527,10 @@ int make_dir(const char *path, mode_t mode) int restorecon(const char* pathname) { - return selinux_android_restorecon(pathname); + return selinux_android_restorecon(pathname, 0); } int restorecon_recursive(const char* pathname) { - return selinux_android_restorecon_recursive(pathname); + return selinux_android_restorecon(pathname, SELINUX_ANDROID_RESTORECON_RECURSE); } diff --git a/libbacktrace/Android.build.mk b/libbacktrace/Android.build.mk new file mode 100644 index 0000000..2f55645 --- /dev/null +++ b/libbacktrace/Android.build.mk @@ -0,0 +1,81 @@ +# +# Copyright (C) 2014 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 $(CLEAR_VARS) + +LOCAL_MODULE := $(module) +LOCAL_MODULE_TAGS := $(module_tag) +LOCAL_MULTILIB := $($(module)_multilib) + +LOCAL_ADDITIONAL_DEPENDENCIES := \ + $(LOCAL_PATH)/Android.mk \ + $(LOCAL_PATH)/Android.build.mk \ + +LOCAL_CFLAGS := \ + $(common_cflags) \ + $($(module)_cflags) \ + $($(module)_cflags_$(build_type)) \ + +LOCAL_CONLYFLAGS += \ + $(common_conlyflags) \ + $($(module)_conlyflags) \ + $($(module)_conlyflags_$(build_type)) \ + +LOCAL_CPPFLAGS += \ + $(common_cppflags) \ + $($(module)_cppflags) \ + $($(module)_cppflags_$(build_type)) \ + +LOCAL_C_INCLUDES := \ + $(common_c_includes) \ + $($(module)_c_includes) \ + $($(module)_c_includes_$(build_type)) \ + +LOCAL_SRC_FILES := \ + $($(module)_src_files) \ + $($(module)_src_files_$(build_type)) \ + +LOCAL_STATIC_LIBRARIES := \ + $($(module)_static_libraries) \ + $($(module)_static_libraries_$(build_type)) \ + +LOCAL_SHARED_LIBRARIES := \ + $($(module)_shared_libraries) \ + $($(module)_shared_libraries_$(build_type)) \ + +LOCAL_LDLIBS := \ + $($(module)_ldlibs) \ + $($(module)_ldlibs_$(build_type)) \ + +ifeq ($(build_type),target) + ifneq ($($(module)_libc++),) + include external/libcxx/libcxx.mk + else + include external/stlport/libstlport.mk + endif + + include $(BUILD_$(build_target)) +endif + +ifeq ($(build_type),host) + # Only build if host builds are supported. + ifeq ($(build_host),true) + ifneq ($($(module)_libc++),) + include external/libcxx/libcxx.mk + endif + include $(BUILD_HOST_$(build_target)) + endif +endif diff --git a/libbacktrace/Android.mk b/libbacktrace/Android.mk index 0ae8839..c321369 100644..100755 --- a/libbacktrace/Android.mk +++ b/libbacktrace/Android.mk @@ -1,14 +1,23 @@ -LOCAL_PATH:= $(call my-dir) +# +# Copyright (C) 2014 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. +# -common_src := \ - BacktraceImpl.cpp \ - BacktraceMap.cpp \ - BacktraceThread.cpp \ - thread_utils.c \ +LOCAL_PATH:= $(call my-dir) common_cflags := \ -Wall \ - -Wno-unused-parameter \ -Werror \ common_conlyflags := \ @@ -17,274 +26,185 @@ common_conlyflags := \ common_cppflags := \ -std=gnu++11 \ -common_shared_libs := \ - libcutils \ - libgccdemangle \ - liblog \ - -# To enable using libunwind on each arch, add it to this list. -libunwind_architectures := arm arm64 +build_host := false +ifeq ($(HOST_OS),linux) +ifeq ($(HOST_ARCH),$(filter $(HOST_ARCH),x86 x86_64)) +build_host := true +endif +endif -ifeq ($(TARGET_ARCH),$(filter $(TARGET_ARCH),$(libunwind_architectures))) +#------------------------------------------------------------------------- +# The libbacktrace library. +#------------------------------------------------------------------------- +libbacktrace_src_files := \ + BacktraceImpl.cpp \ + BacktraceMap.cpp \ + BacktraceThread.cpp \ + thread_utils.c \ -#---------------------------------------------------------------------------- -# The native libbacktrace library with libunwind. -#---------------------------------------------------------------------------- -include $(CLEAR_VARS) +libbacktrace_shared_libraries_target := \ + libcutils \ + libgccdemangle \ -LOCAL_SRC_FILES:= \ - $(common_src) \ +libbacktrace_src_files += \ UnwindCurrent.cpp \ UnwindMap.cpp \ UnwindPtrace.cpp \ -LOCAL_CFLAGS := \ - $(common_cflags) \ - -LOCAL_CONLYFLAGS += \ - $(common_conlyflags) \ - -LOCAL_CPPFLAGS += \ - $(common_cppflags) \ - -LOCAL_MODULE := libbacktrace -LOCAL_MODULE_TAGS := optional - -LOCAL_C_INCLUDES := \ - $(common_c_includes) \ +libbacktrace_c_includes := \ external/libunwind/include \ -LOCAL_SHARED_LIBRARIES := \ - $(common_shared_libs) \ +libbacktrace_shared_libraries := \ libunwind \ libunwind-ptrace \ -LOCAL_ADDITIONAL_DEPENDENCIES := \ - $(LOCAL_PATH)/Android.mk - -include external/stlport/libstlport.mk - -include $(BUILD_SHARED_LIBRARY) - -else - -#---------------------------------------------------------------------------- -# The native libbacktrace library with libcorkscrew. -#---------------------------------------------------------------------------- -include $(CLEAR_VARS) - -LOCAL_SRC_FILES:= \ - $(common_src) \ - Corkscrew.cpp \ - -LOCAL_CFLAGS := \ - $(common_cflags) \ +libbacktrace_shared_libraries_host := \ + liblog \ -LOCAL_CONLYFLAGS += \ - $(common_conlyflags) \ +libbacktrace_static_libraries_host := \ + libcutils \ -LOCAL_CPPFLAGS += \ - $(common_cppflags) \ +module := libbacktrace +module_tag := optional +build_type := target +build_target := SHARED_LIBRARY +include $(LOCAL_PATH)/Android.build.mk +build_type := host +include $(LOCAL_PATH)/Android.build.mk + +# Don't build for unbundled branches +ifeq (,$(TARGET_BUILD_APPS)) +#------------------------------------------------------------------------- +# The libbacktrace library (libc++) +#------------------------------------------------------------------------- +libbacktrace_libc++_src_files := \ + BacktraceImpl.cpp \ + BacktraceMap.cpp \ + BacktraceThread.cpp \ + thread_utils.c \ -LOCAL_MODULE := libbacktrace -LOCAL_MODULE_TAGS := optional +libbacktrace_libc++_shared_libraries_target := \ + libcutils \ + libgccdemangle \ -LOCAL_C_INCLUDES := \ - $(common_c_includes) \ - system/core/libcorkscrew \ +libbacktrace_libc++_src_files += \ + UnwindCurrent.cpp \ + UnwindMap.cpp \ + UnwindPtrace.cpp \ -LOCAL_SHARED_LIBRARIES := \ - $(common_shared_libs) \ - libcorkscrew \ - libdl \ +libbacktrace_libc++_c_includes := \ + external/libunwind/include \ -LOCAL_ADDITIONAL_DEPENDENCIES := \ - $(LOCAL_PATH)/Android.mk +libbacktrace_libc++_shared_libraries := \ + libunwind \ + libunwind-ptrace \ -include external/stlport/libstlport.mk +libbacktrace_libc++_shared_libraries_host := \ + liblog \ -include $(BUILD_SHARED_LIBRARY) +libbacktrace_libc++_static_libraries_host := \ + libcutils \ +libbacktrace_libc++_libc++ := true + +module := libbacktrace_libc++ +module_tag := optional +build_type := target +build_target := SHARED_LIBRARY +include $(LOCAL_PATH)/Android.build.mk +build_type := host +libbacktrace_libc++_multilib := both +include $(LOCAL_PATH)/Android.build.mk +libbacktrace_libc++_multilib := endif -#---------------------------------------------------------------------------- -# libbacktrace test library, all optimizations turned off -#---------------------------------------------------------------------------- -include $(CLEAR_VARS) - -LOCAL_MODULE := libbacktrace_test -LOCAL_MODULE_FLAGS := debug - -LOCAL_SRC_FILES := \ - backtrace_testlib.c - -LOCAL_CFLAGS += \ - -std=gnu99 \ +#------------------------------------------------------------------------- +# The libbacktrace_test library needed by backtrace_test. +#------------------------------------------------------------------------- +libbacktrace_test_cflags := \ -O0 \ -LOCAL_ADDITIONAL_DEPENDENCIES := \ - $(LOCAL_PATH)/Android.mk - -include $(BUILD_SHARED_LIBRARY) - -#---------------------------------------------------------------------------- -# libbacktrace test executable -#---------------------------------------------------------------------------- -include $(CLEAR_VARS) - -LOCAL_MODULE := backtrace_test -LOCAL_MODULE_FLAGS := debug - -LOCAL_SRC_FILES := \ - backtrace_test.cpp \ - thread_utils.c \ - -LOCAL_CFLAGS += \ - $(common_cflags) \ +libbacktrace_test_src_files := \ + backtrace_testlib.c \ + +module := libbacktrace_test +module_tag := debug +build_type := target +build_target := SHARED_LIBRARY +include $(LOCAL_PATH)/Android.build.mk +build_type := host +include $(LOCAL_PATH)/Android.build.mk + +#------------------------------------------------------------------------- +# The backtrace_test executable. +#------------------------------------------------------------------------- +backtrace_test_cflags := \ -fno-builtin \ - -fstack-protector-all \ -O0 \ -g \ - -DGTEST_OS_LINUX_ANDROID \ - -DGTEST_HAS_STD_STRING \ -ifeq ($(TARGET_ARCH),arm64) - $(info TODO: $(LOCAL_PATH)/Android.mk -fstack-protector not yet available for the AArch64 toolchain) - LOCAL_CFLAGS += -fno-stack-protector -endif # arm64 +backtrace_test_cflags_target := \ + -DENABLE_PSS_TESTS \ -LOCAL_CONLYFLAGS += \ - $(common_conlyflags) \ +backtrace_test_src_files := \ + backtrace_test.cpp \ + GetPss.cpp \ + thread_utils.c \ -LOCAL_CPPFLAGS += \ - $(common_cppflags) \ +backtrace_test_ldlibs_host := \ + -lpthread \ + -lrt \ -LOCAL_SHARED_LIBRARIES += \ - libcutils \ +backtrace_test_shared_libraries := \ libbacktrace_test \ libbacktrace \ -LOCAL_LDLIBS := \ - -lpthread \ +backtrace_test_shared_libraries_target := \ + libcutils \ -LOCAL_ADDITIONAL_DEPENDENCIES := \ - $(LOCAL_PATH)/Android.mk +backtrace_test_static_libraries_host := \ + libcutils \ -include $(BUILD_NATIVE_TEST) +module := backtrace_test +module_tag := debug +build_type := target +build_target := NATIVE_TEST +include $(LOCAL_PATH)/Android.build.mk +build_type := host +include $(LOCAL_PATH)/Android.build.mk #---------------------------------------------------------------------------- -# Only x86 host versions of libbacktrace supported. +# Special truncated libbacktrace library for mac. #---------------------------------------------------------------------------- -ifeq ($(HOST_ARCH),x86) +ifeq ($(HOST_OS),darwin) -#---------------------------------------------------------------------------- -# The host libbacktrace library using libcorkscrew -#---------------------------------------------------------------------------- include $(CLEAR_VARS) - -LOCAL_CFLAGS += \ - $(common_cflags) \ - -LOCAL_CONLYFLAGS += \ - $(common_conlyflags) \ - -LOCAL_C_INCLUDES := \ - $(common_c_includes) \ - -LOCAL_SHARED_LIBRARIES := \ - libgccdemangle \ - liblog \ - LOCAL_MODULE := libbacktrace LOCAL_MODULE_TAGS := optional -LOCAL_ADDITIONAL_DEPENDENCIES := \ - $(LOCAL_PATH)/Android.mk - -ifeq ($(HOST_OS),linux) -LOCAL_SRC_FILES += \ - $(common_src) \ - Corkscrew.cpp \ - -LOCAL_C_INCLUDES += \ - system/core/libcorkscrew \ - -LOCAL_SHARED_LIBRARIES := \ - libcorkscrew \ - -LOCAL_CPPFLAGS += \ - $(common_cppflags) \ - -LOCAL_LDLIBS += \ - -ldl \ - -lrt \ - -else -LOCAL_SRC_FILES += \ +LOCAL_SRC_FILES := \ BacktraceMap.cpp \ -endif - include $(BUILD_HOST_SHARED_LIBRARY) -#---------------------------------------------------------------------------- -# The host test is only supported on linux. -#---------------------------------------------------------------------------- -ifeq ($(HOST_OS),linux) - -#---------------------------------------------------------------------------- -# libbacktrace host test library, all optimizations turned off -#---------------------------------------------------------------------------- +# Don't build for unbundled branches +ifeq (,$(TARGET_BUILD_APPS)) +#------------------------------------------------------------------------- +# The libbacktrace library (libc++) +#------------------------------------------------------------------------- include $(CLEAR_VARS) -LOCAL_MODULE := libbacktrace_test -LOCAL_MODULE_FLAGS := debug +LOCAL_MODULE := libbacktrace_libc++ +LOCAL_MODULE_TAGS := optional LOCAL_SRC_FILES := \ - backtrace_testlib.c - -LOCAL_CFLAGS += \ - -std=gnu99 \ - -O0 \ + BacktraceMap.cpp \ -LOCAL_ADDITIONAL_DEPENDENCIES := \ - $(LOCAL_PATH)/Android.mk +LOCAL_MULTILIB := both include $(BUILD_HOST_SHARED_LIBRARY) -#---------------------------------------------------------------------------- -# libbacktrace host test executable -#---------------------------------------------------------------------------- -include $(CLEAR_VARS) - -LOCAL_MODULE := backtrace_test -LOCAL_MODULE_FLAGS := debug - -LOCAL_SRC_FILES := \ - backtrace_test.cpp \ - thread_utils.c \ - -LOCAL_CFLAGS += \ - $(common_cflags) \ - -fno-builtin \ - -fstack-protector-all \ - -O0 \ - -g \ - -DGTEST_HAS_STD_STRING \ - -LOCAL_SHARED_LIBRARIES := \ - libbacktrace_test \ - libbacktrace \ - -LOCAL_LDLIBS := \ - -lpthread \ - -LOCAL_ADDITIONAL_DEPENDENCIES := \ - $(LOCAL_PATH)/Android.mk - -include $(BUILD_HOST_NATIVE_TEST) - -endif # HOST_OS == linux +endif # TARGET_BUILD_APPS -endif # HOST_ARCH == x86 +endif # HOST_OS-darwin diff --git a/libbacktrace/BacktraceImpl.cpp b/libbacktrace/BacktraceImpl.cpp index 39296b4..405b042 100644 --- a/libbacktrace/BacktraceImpl.cpp +++ b/libbacktrace/BacktraceImpl.cpp @@ -19,17 +19,16 @@ #include <string.h> #include <sys/ptrace.h> #include <sys/types.h> +#include <ucontext.h> #include <unistd.h> -#define __STDC_FORMAT_MACROS -#include <inttypes.h> - #include <string> #include <backtrace/Backtrace.h> #include <backtrace/BacktraceMap.h> #include "BacktraceImpl.h" +#include "BacktraceLog.h" #include "thread_utils.h" //------------------------------------------------------------------------- @@ -57,8 +56,8 @@ Backtrace::~Backtrace() { } } -bool Backtrace::Unwind(size_t num_ignore_frames) { - return impl_->Unwind(num_ignore_frames); +bool Backtrace::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) { + return impl_->Unwind(num_ignore_frames, ucontext); } extern "C" char* __cxa_demangle(const char* mangled, char* buf, size_t* len, @@ -82,10 +81,10 @@ std::string Backtrace::GetFunctionName(uintptr_t pc, uintptr_t* offset) { return func_name; } -bool Backtrace::VerifyReadWordArgs(uintptr_t ptr, uint32_t* out_value) { - if (ptr & 3) { +bool Backtrace::VerifyReadWordArgs(uintptr_t ptr, word_t* out_value) { + if (ptr & (sizeof(word_t)-1)) { BACK_LOGW("invalid pointer %p", (void*)ptr); - *out_value = (uint32_t)-1; + *out_value = (word_t)-1; return false; } return true; @@ -143,18 +142,18 @@ BacktraceCurrent::BacktraceCurrent( BacktraceCurrent::~BacktraceCurrent() { } -bool BacktraceCurrent::ReadWord(uintptr_t ptr, uint32_t* out_value) { +bool BacktraceCurrent::ReadWord(uintptr_t ptr, word_t* out_value) { if (!VerifyReadWordArgs(ptr, out_value)) { return false; } const backtrace_map_t* map = FindMap(ptr); if (map && map->flags & PROT_READ) { - *out_value = *reinterpret_cast<uint32_t*>(ptr); + *out_value = *reinterpret_cast<word_t*>(ptr); return true; } else { BACK_LOGW("pointer %p not in a readable map", reinterpret_cast<void*>(ptr)); - *out_value = static_cast<uint32_t>(-1); + *out_value = static_cast<word_t>(-1); return false; } } @@ -171,7 +170,7 @@ BacktracePtrace::BacktracePtrace( BacktracePtrace::~BacktracePtrace() { } -bool BacktracePtrace::ReadWord(uintptr_t ptr, uint32_t* out_value) { +bool BacktracePtrace::ReadWord(uintptr_t ptr, word_t* out_value) { if (!VerifyReadWordArgs(ptr, out_value)) { return false; } @@ -184,7 +183,7 @@ bool BacktracePtrace::ReadWord(uintptr_t ptr, uint32_t* out_value) { // To disambiguate -1 from a valid result, we clear errno beforehand. errno = 0; *out_value = ptrace(PTRACE_PEEKTEXT, Tid(), reinterpret_cast<void*>(ptr), NULL); - if (*out_value == static_cast<uint32_t>(-1) && errno) { + if (*out_value == static_cast<word_t>(-1) && errno) { BACK_LOGW("invalid pointer %p reading from tid %d, ptrace() strerror(errno)=%s", reinterpret_cast<void*>(ptr), Tid(), strerror(errno)); return false; diff --git a/libbacktrace/BacktraceImpl.h b/libbacktrace/BacktraceImpl.h index af014d5..d34ad05 100644..100755 --- a/libbacktrace/BacktraceImpl.h +++ b/libbacktrace/BacktraceImpl.h @@ -21,17 +21,12 @@ #include <backtrace/BacktraceMap.h> #include <sys/types.h> -#include <log/log.h> - -// Macro to log the function name along with the warning message. -#define BACK_LOGW(format, ...) \ - ALOGW("%s: " format, __PRETTY_FUNCTION__, ##__VA_ARGS__) class BacktraceImpl { public: virtual ~BacktraceImpl() { } - virtual bool Unwind(size_t num_ignore_frames) = 0; + virtual bool Unwind(size_t num_ignore_frames, ucontext_t* ucontext) = 0; // The name returned is not demangled, Backtrace::GetFunctionName() // takes care of demangling the name. @@ -61,7 +56,7 @@ public: BacktraceCurrent(BacktraceImpl* impl, BacktraceMap* map); virtual ~BacktraceCurrent(); - bool ReadWord(uintptr_t ptr, uint32_t* out_value); + bool ReadWord(uintptr_t ptr, word_t* out_value); }; class BacktracePtrace : public Backtrace { @@ -69,7 +64,7 @@ public: BacktracePtrace(BacktraceImpl* impl, pid_t pid, pid_t tid, BacktraceMap* map); virtual ~BacktracePtrace(); - bool ReadWord(uintptr_t ptr, uint32_t* out_value); + bool ReadWord(uintptr_t ptr, word_t* out_value); }; Backtrace* CreateCurrentObj(BacktraceMap* map); diff --git a/libbacktrace/BacktraceLog.h b/libbacktrace/BacktraceLog.h new file mode 100755 index 0000000..1632ec2 --- /dev/null +++ b/libbacktrace/BacktraceLog.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2014 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. + */ + +#ifndef _LIBBACKTRACE_BACKTRACE_LOG_H +#define _LIBBACKTRACE_BACKTRACE_LOG_H + +#define LOG_TAG "libbacktrace" + +#include <log/log.h> + +// Macro to log the function name along with the warning message. +#define BACK_LOGW(format, ...) \ + ALOGW("%s: " format, __PRETTY_FUNCTION__, ##__VA_ARGS__) + +#endif // _LIBBACKTRACE_BACKTRACE_LOG_H diff --git a/libbacktrace/BacktraceMap.cpp b/libbacktrace/BacktraceMap.cpp index 6320800..0056f4b 100644 --- a/libbacktrace/BacktraceMap.cpp +++ b/libbacktrace/BacktraceMap.cpp @@ -93,7 +93,8 @@ bool BacktraceMap::ParseLine(const char* line, backtrace_map_t* map) { } ALOGV("Parsed map: start=%p, end=%p, flags=%x, name=%s", - map->start, map->end, map->flags, map->name.c_str()); + reinterpret_cast<void*>(map->start), reinterpret_cast<void*>(map->end), + map->flags, map->name.c_str()); return true; } @@ -107,11 +108,11 @@ bool BacktraceMap::Build() { #if defined(__APPLE__) // cmd is guaranteed to always be big enough to hold this string. - sprintf(cmd, "vmmap -w -resident -submap -allSplitLibs -interleaved %d", pid_); + snprintf(cmd, sizeof(cmd), "vmmap -w -resident -submap -allSplitLibs -interleaved %d", pid_); FILE* fp = popen(cmd, "r"); #else // path is guaranteed to always be big enough to hold this string. - sprintf(path, "/proc/%d/maps", pid_); + snprintf(path, sizeof(path), "/proc/%d/maps", pid_); FILE* fp = fopen(path, "r"); #endif if (fp == NULL) { diff --git a/libbacktrace/BacktraceThread.cpp b/libbacktrace/BacktraceThread.cpp index 5ffe516..b47cd2a 100644 --- a/libbacktrace/BacktraceThread.cpp +++ b/libbacktrace/BacktraceThread.cpp @@ -16,200 +16,195 @@ #include <errno.h> #include <inttypes.h> +#include <limits.h> +#include <linux/futex.h> #include <pthread.h> #include <signal.h> #include <string.h> +#include <sys/syscall.h> +#include <sys/time.h> #include <sys/types.h> +#include <ucontext.h> #include <cutils/atomic.h> +#include "BacktraceLog.h" #include "BacktraceThread.h" #include "thread_utils.h" +static inline int futex(volatile int* uaddr, int op, int val, const struct timespec* ts, volatile int* uaddr2, int val3) { + return syscall(__NR_futex, uaddr, op, val, ts, uaddr2, val3); +} + //------------------------------------------------------------------------- // ThreadEntry implementation. //------------------------------------------------------------------------- -static ThreadEntry* g_list = NULL; -static pthread_mutex_t g_entry_mutex = PTHREAD_MUTEX_INITIALIZER; -static pthread_mutex_t g_sigaction_mutex = PTHREAD_MUTEX_INITIALIZER; - -ThreadEntry::ThreadEntry( - BacktraceThreadInterface* intf, pid_t pid, pid_t tid, size_t num_ignore_frames) - : thread_intf(intf), pid(pid), tid(tid), next(NULL), prev(NULL), - state(STATE_WAITING), num_ignore_frames(num_ignore_frames) { +ThreadEntry* ThreadEntry::list_ = NULL; +pthread_mutex_t ThreadEntry::list_mutex_ = PTHREAD_MUTEX_INITIALIZER; + +// Assumes that ThreadEntry::list_mutex_ has already been locked before +// creating a ThreadEntry object. +ThreadEntry::ThreadEntry(pid_t pid, pid_t tid) + : pid_(pid), tid_(tid), futex_(0), ref_count_(1), mutex_(PTHREAD_MUTEX_INITIALIZER), next_(ThreadEntry::list_), prev_(NULL) { + // Add ourselves to the list. + if (ThreadEntry::list_) { + ThreadEntry::list_->prev_ = this; + } + ThreadEntry::list_ = this; } -ThreadEntry::~ThreadEntry() { - pthread_mutex_lock(&g_entry_mutex); - if (g_list == this) { - g_list = next; - } else { - if (next) { - next->prev = prev; +ThreadEntry* ThreadEntry::Get(pid_t pid, pid_t tid, bool create) { + pthread_mutex_lock(&ThreadEntry::list_mutex_); + ThreadEntry* entry = list_; + while (entry != NULL) { + if (entry->Match(pid, tid)) { + break; } - prev->next = next; + entry = entry->next_; } - pthread_mutex_unlock(&g_entry_mutex); - next = NULL; - prev = NULL; + if (!entry) { + if (create) { + entry = new ThreadEntry(pid, tid); + } + } else { + entry->ref_count_++; + } + pthread_mutex_unlock(&ThreadEntry::list_mutex_); + + return entry; } -ThreadEntry* ThreadEntry::AddThreadToUnwind( - BacktraceThreadInterface* intf, pid_t pid, pid_t tid, size_t num_ignore_frames) { - ThreadEntry* entry = new ThreadEntry(intf, pid, tid, num_ignore_frames); +void ThreadEntry::Remove(ThreadEntry* entry) { + pthread_mutex_unlock(&entry->mutex_); - pthread_mutex_lock(&g_entry_mutex); - ThreadEntry* cur_entry = g_list; - while (cur_entry != NULL) { - if (cur_entry->Match(pid, tid)) { - // There is already an entry for this pid/tid, this is bad. - BACK_LOGW("Entry for pid %d tid %d already exists.", pid, tid); + pthread_mutex_lock(&ThreadEntry::list_mutex_); + if (--entry->ref_count_ == 0) { + delete entry; + } + pthread_mutex_unlock(&ThreadEntry::list_mutex_); +} - pthread_mutex_unlock(&g_entry_mutex); - return NULL; +// Assumes that ThreadEntry::list_mutex_ has already been locked before +// deleting a ThreadEntry object. +ThreadEntry::~ThreadEntry() { + if (list_ == this) { + list_ = next_; + } else { + if (next_) { + next_->prev_ = prev_; } - cur_entry = cur_entry->next; + prev_->next_ = next_; } - // Add the entry to the list. - entry->next = g_list; - if (g_list) { - g_list->prev = entry; + next_ = NULL; + prev_ = NULL; +} + +void ThreadEntry::Wait(int value) { + timespec ts; + ts.tv_sec = 10; + ts.tv_nsec = 0; + errno = 0; + futex(&futex_, FUTEX_WAIT, value, &ts, NULL, 0); + if (errno != 0 && errno != EWOULDBLOCK) { + BACK_LOGW("futex wait failed, futex = %d: %s", futex_, strerror(errno)); } - g_list = entry; - pthread_mutex_unlock(&g_entry_mutex); +} - return entry; +void ThreadEntry::Wake() { + futex_++; + futex(&futex_, FUTEX_WAKE, INT_MAX, NULL, NULL, 0); +} + +void ThreadEntry::CopyUcontextFromSigcontext(void* sigcontext) { + ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(sigcontext); + // The only thing the unwinder cares about is the mcontext data. + memcpy(&ucontext_.uc_mcontext, &ucontext->uc_mcontext, sizeof(ucontext->uc_mcontext)); } //------------------------------------------------------------------------- // BacktraceThread functions. //------------------------------------------------------------------------- -static void SignalHandler(int n __attribute__((unused)), siginfo_t* siginfo, - void* sigcontext) { - if (pthread_mutex_lock(&g_entry_mutex) == 0) { - pid_t pid = getpid(); - pid_t tid = gettid(); - ThreadEntry* cur_entry = g_list; - while (cur_entry) { - if (cur_entry->Match(pid, tid)) { - break; - } - cur_entry = cur_entry->next; - } - pthread_mutex_unlock(&g_entry_mutex); - if (!cur_entry) { - BACK_LOGW("Unable to find pid %d tid %d information", pid, tid); - return; - } +static pthread_mutex_t g_sigaction_mutex = PTHREAD_MUTEX_INITIALIZER; - if (android_atomic_acquire_cas(STATE_WAITING, STATE_DUMPING, &cur_entry->state) == 0) { - cur_entry->thread_intf->ThreadUnwind(siginfo, sigcontext, - cur_entry->num_ignore_frames); - } - android_atomic_release_store(STATE_DONE, &cur_entry->state); +static void SignalHandler(int, siginfo_t*, void* sigcontext) { + ThreadEntry* entry = ThreadEntry::Get(getpid(), gettid(), false); + if (!entry) { + BACK_LOGW("Unable to find pid %d tid %d information", getpid(), gettid()); + return; } + + entry->CopyUcontextFromSigcontext(sigcontext); + + // Indicate the ucontext is now valid. + entry->Wake(); + + // Pause the thread until the unwind is complete. This avoids having + // the thread run ahead causing problems. + entry->Wait(1); + + ThreadEntry::Remove(entry); } -BacktraceThread::BacktraceThread( - BacktraceImpl* impl, BacktraceThreadInterface* thread_intf, pid_t tid, - BacktraceMap* map) - : BacktraceCurrent(impl, map), thread_intf_(thread_intf) { +BacktraceThread::BacktraceThread(BacktraceImpl* impl, pid_t tid, BacktraceMap* map) + : BacktraceCurrent(impl, map) { tid_ = tid; } BacktraceThread::~BacktraceThread() { } -void BacktraceThread::FinishUnwind() { - for (std::vector<backtrace_frame_data_t>::iterator it = frames_.begin(); - it != frames_.end(); ++it) { - it->map = FindMap(it->pc); - - it->func_offset = 0; - it->func_name = GetFunctionName(it->pc, &it->func_offset); +bool BacktraceThread::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) { + if (ucontext) { + // Unwind using an already existing ucontext. + return impl_->Unwind(num_ignore_frames, ucontext); } -} -bool BacktraceThread::TriggerUnwindOnThread(ThreadEntry* entry) { - entry->state = STATE_WAITING; - - if (tgkill(Pid(), Tid(), SIGURG) != 0) { - BACK_LOGW("tgkill failed %s", strerror(errno)); + // Prevent multiple threads trying to set the trigger action on different + // threads at the same time. + if (pthread_mutex_lock(&g_sigaction_mutex) < 0) { + BACK_LOGW("sigaction failed: %s", strerror(errno)); return false; } - // Allow up to ten seconds for the dump to start. - int wait_millis = 10000; - int32_t state; - while (true) { - state = android_atomic_acquire_load(&entry->state); - if (state != STATE_WAITING) { - break; - } - if (wait_millis--) { - usleep(1000); - } else { - break; - } + ThreadEntry* entry = ThreadEntry::Get(Pid(), Tid()); + entry->Lock(); + + struct sigaction act, oldact; + memset(&act, 0, sizeof(act)); + act.sa_sigaction = SignalHandler; + act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK; + sigemptyset(&act.sa_mask); + if (sigaction(THREAD_SIGNAL, &act, &oldact) != 0) { + BACK_LOGW("sigaction failed %s", strerror(errno)); + entry->Unlock(); + ThreadEntry::Remove(entry); + pthread_mutex_unlock(&g_sigaction_mutex); + return false; } - bool cancelled = false; - if (state == STATE_WAITING) { - if (android_atomic_acquire_cas(state, STATE_CANCEL, &entry->state) == 0) { - BACK_LOGW("Cancelled dump of thread %d", entry->tid); - state = STATE_CANCEL; - cancelled = true; - } else { - state = android_atomic_acquire_load(&entry->state); - } + if (tgkill(Pid(), Tid(), THREAD_SIGNAL) != 0) { + BACK_LOGW("tgkill %d failed: %s", Tid(), strerror(errno)); + sigaction(THREAD_SIGNAL, &oldact, NULL); + entry->Unlock(); + ThreadEntry::Remove(entry); + pthread_mutex_unlock(&g_sigaction_mutex); + return false; } - // Wait for at most ten seconds for the cancel or dump to finish. - wait_millis = 10000; - while (android_atomic_acquire_load(&entry->state) != STATE_DONE) { - if (wait_millis--) { - usleep(1000); - } else { - BACK_LOGW("Didn't finish thread unwind in 60 seconds."); - break; - } - } - return !cancelled; -} + // Wait for the thread to get the ucontext. + entry->Wait(0); -bool BacktraceThread::Unwind(size_t num_ignore_frames) { - ThreadEntry* entry = ThreadEntry::AddThreadToUnwind( - thread_intf_, Pid(), Tid(), num_ignore_frames); - if (!entry) { - return false; - } + // After the thread has received the signal, allow other unwinders to + // continue. + sigaction(THREAD_SIGNAL, &oldact, NULL); + pthread_mutex_unlock(&g_sigaction_mutex); - // Prevent multiple threads trying to set the trigger action on different - // threads at the same time. - bool retval = false; - if (pthread_mutex_lock(&g_sigaction_mutex) == 0) { - struct sigaction act, oldact; - memset(&act, 0, sizeof(act)); - act.sa_sigaction = SignalHandler; - act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK; - sigemptyset(&act.sa_mask); - if (sigaction(SIGURG, &act, &oldact) == 0) { - retval = TriggerUnwindOnThread(entry); - sigaction(SIGURG, &oldact, NULL); - } else { - BACK_LOGW("sigaction failed %s", strerror(errno)); - } - pthread_mutex_unlock(&g_sigaction_mutex); - } else { - BACK_LOGW("unable to acquire sigaction mutex."); - } + bool unwind_done = impl_->Unwind(num_ignore_frames, entry->GetUcontext()); - if (retval) { - FinishUnwind(); - } - delete entry; + // Tell the signal handler to exit and release the entry. + entry->Wake(); - return retval; + return unwind_done; } diff --git a/libbacktrace/BacktraceThread.h b/libbacktrace/BacktraceThread.h index 3412d58..ff3e9f3 100644 --- a/libbacktrace/BacktraceThread.h +++ b/libbacktrace/BacktraceThread.h @@ -18,73 +18,71 @@ #define _LIBBACKTRACE_BACKTRACE_THREAD_H #include <inttypes.h> +#include <pthread.h> +#include <signal.h> +#include <string.h> #include <sys/types.h> +#include <ucontext.h> #include "BacktraceImpl.h" -enum state_e { - STATE_WAITING = 0, - STATE_DUMPING, - STATE_DONE, - STATE_CANCEL, -}; +// The signal used to cause a thread to dump the stack. +#if defined(__GLIBC__) +// GLIBC reserves __SIGRTMIN signals, so use SIGRTMIN to avoid errors. +#define THREAD_SIGNAL SIGRTMIN +#else +#define THREAD_SIGNAL (__SIGRTMIN+1) +#endif -class BacktraceThreadInterface; +class ThreadEntry { +public: + static ThreadEntry* Get(pid_t pid, pid_t tid, bool create = true); -struct ThreadEntry { - ThreadEntry( - BacktraceThreadInterface* impl, pid_t pid, pid_t tid, - size_t num_ignore_frames); - ~ThreadEntry(); + static void Remove(ThreadEntry* entry); - bool Match(pid_t chk_pid, pid_t chk_tid) { return (chk_pid == pid && chk_tid == tid); } + void Wake(); - static ThreadEntry* AddThreadToUnwind( - BacktraceThreadInterface* thread_intf, pid_t pid, pid_t tid, - size_t num_ignored_frames); + void Wait(int); - BacktraceThreadInterface* thread_intf; - pid_t pid; - pid_t tid; - ThreadEntry* next; - ThreadEntry* prev; - int32_t state; - int num_ignore_frames; -}; + void CopyUcontextFromSigcontext(void*); -// Interface class that does not contain any local storage, only defines -// virtual functions to be defined by subclasses. -class BacktraceThreadInterface { -public: - virtual ~BacktraceThreadInterface() { } + inline void Lock() { + pthread_mutex_lock(&mutex_); + // Reset the futex value in case of multiple unwinds of the same thread. + futex_ = 0; + } - virtual void ThreadUnwind( - siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames) = 0; -}; + inline void Unlock() { + pthread_mutex_unlock(&mutex_); + } -class BacktraceThread : public BacktraceCurrent { -public: - // impl and thread_intf should point to the same object, this allows - // the compiler to catch if an implementation does not properly - // subclass both. - BacktraceThread( - BacktraceImpl* impl, BacktraceThreadInterface* thread_intf, pid_t tid, - BacktraceMap* map); - virtual ~BacktraceThread(); + inline ucontext_t* GetUcontext() { return &ucontext_; } - virtual bool Unwind(size_t num_ignore_frames); +private: + ThreadEntry(pid_t pid, pid_t tid); + ~ThreadEntry(); - virtual void ThreadUnwind( - siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames) { - thread_intf_->ThreadUnwind(siginfo, sigcontext, num_ignore_frames); - } + bool Match(pid_t chk_pid, pid_t chk_tid) { return (chk_pid == pid_ && chk_tid == tid_); } -private: - virtual bool TriggerUnwindOnThread(ThreadEntry* entry); + pid_t pid_; + pid_t tid_; + int futex_; + int ref_count_; + pthread_mutex_t mutex_; + ThreadEntry* next_; + ThreadEntry* prev_; + ucontext_t ucontext_; - virtual void FinishUnwind(); + static ThreadEntry* list_; + static pthread_mutex_t list_mutex_; +}; + +class BacktraceThread : public BacktraceCurrent { +public: + BacktraceThread(BacktraceImpl* impl, pid_t tid, BacktraceMap* map); + virtual ~BacktraceThread(); - BacktraceThreadInterface* thread_intf_; + virtual bool Unwind(size_t num_ignore_frames, ucontext_t* ucontext); }; #endif // _LIBBACKTRACE_BACKTRACE_THREAD_H diff --git a/libbacktrace/Corkscrew.cpp b/libbacktrace/Corkscrew.cpp deleted file mode 100644 index efeee2e..0000000 --- a/libbacktrace/Corkscrew.cpp +++ /dev/null @@ -1,251 +0,0 @@ -/* - * Copyright (C) 2013 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. - */ - -#define LOG_TAG "libbacktrace" - -#include <backtrace/Backtrace.h> - -#include <string.h> - -#include <backtrace-arch.h> -#include <corkscrew/backtrace.h> - -#ifndef __USE_GNU -#define __USE_GNU -#endif -#include <dlfcn.h> - -#include "Corkscrew.h" - -//------------------------------------------------------------------------- -// CorkscrewMap functions. -//------------------------------------------------------------------------- -CorkscrewMap::CorkscrewMap(pid_t pid) : BacktraceMap(pid), map_info_(NULL) { -} - -CorkscrewMap::~CorkscrewMap() { - if (map_info_) { - free_map_info_list(map_info_); - map_info_ = NULL; - } -} - -bool CorkscrewMap::Build() { - map_info_ = load_map_info_list(pid_); - - // Use the information in map_info_ to construct the BacktraceMap data - // rather than reparsing /proc/self/maps. - map_info_t* cur_map = map_info_; - while (cur_map) { - backtrace_map_t map; - map.start = cur_map->start; - map.end = cur_map->end; - map.flags = 0; - if (cur_map->is_readable) { - map.flags |= PROT_READ; - } - if (cur_map->is_writable) { - map.flags |= PROT_WRITE; - } - if (cur_map->is_executable) { - map.flags |= PROT_EXEC; - } - map.name = cur_map->name; - - // The maps are in descending order, but we want them in ascending order. - maps_.push_front(map); - - cur_map = cur_map->next; - } - return map_info_ != NULL; -} - -//------------------------------------------------------------------------- -// CorkscrewCommon functions. -//------------------------------------------------------------------------- -bool CorkscrewCommon::GenerateFrameData( - backtrace_frame_t* cork_frames, ssize_t num_frames) { - if (num_frames < 0) { - BACK_LOGW("libcorkscrew unwind failed."); - return false; - } - - std::vector<backtrace_frame_data_t>* frames = GetFrames(); - frames->resize(num_frames); - size_t i = 0; - for (std::vector<backtrace_frame_data_t>::iterator it = frames->begin(); - it != frames->end(); ++it, ++i) { - it->num = i; - it->pc = cork_frames[i].absolute_pc; - it->sp = cork_frames[i].stack_top; - it->stack_size = cork_frames[i].stack_size; - it->func_offset = 0; - - it->map = FindMap(it->pc); - it->func_name = GetFunctionName(it->pc, &it->func_offset); - } - return true; -} - -//------------------------------------------------------------------------- -// CorkscrewCurrent functions. -//------------------------------------------------------------------------- -CorkscrewCurrent::CorkscrewCurrent() { -} - -CorkscrewCurrent::~CorkscrewCurrent() { -} - -bool CorkscrewCurrent::Unwind(size_t num_ignore_frames) { - backtrace_frame_t frames[MAX_BACKTRACE_FRAMES]; - ssize_t num_frames = unwind_backtrace(frames, num_ignore_frames, MAX_BACKTRACE_FRAMES); - - return GenerateFrameData(frames, num_frames); -} - -std::string CorkscrewCurrent::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) { - *offset = 0; - - Dl_info info; - const backtrace_map_t* map = FindMap(pc); - if (map) { - if (dladdr((const void*)pc, &info)) { - if (info.dli_sname) { - *offset = pc - map->start - (uintptr_t)info.dli_saddr + (uintptr_t)info.dli_fbase; - return info.dli_sname; - } - } else { - // dladdr(3) didn't find a symbol; maybe it's static? Look in the ELF file... - symbol_table_t* symbol_table = load_symbol_table(map->name.c_str()); - if (symbol_table) { - // First check if we can find the symbol using a relative pc. - std::string name; - const symbol_t* elf_symbol = find_symbol(symbol_table, pc - map->start); - if (elf_symbol) { - name = elf_symbol->name; - *offset = pc - map->start - elf_symbol->start; - } else if ((elf_symbol = find_symbol(symbol_table, pc)) != NULL) { - // Found the symbol using the absolute pc. - name = elf_symbol->name; - *offset = pc - elf_symbol->start; - } - free_symbol_table(symbol_table); - return name; - } - } - } - return ""; -} - -//------------------------------------------------------------------------- -// CorkscrewThread functions. -//------------------------------------------------------------------------- -CorkscrewThread::CorkscrewThread() { -} - -CorkscrewThread::~CorkscrewThread() { -} - -void CorkscrewThread::ThreadUnwind( - siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames) { - backtrace_frame_t cork_frames[MAX_BACKTRACE_FRAMES]; - CorkscrewMap* map = static_cast<CorkscrewMap*>(GetMap()); - ssize_t num_frames = unwind_backtrace_signal_arch( - siginfo, sigcontext, map->GetMapInfo(), cork_frames, - num_ignore_frames, MAX_BACKTRACE_FRAMES); - if (num_frames > 0) { - std::vector<backtrace_frame_data_t>* frames = GetFrames(); - frames->resize(num_frames); - size_t i = 0; - for (std::vector<backtrace_frame_data_t>::iterator it = frames->begin(); - it != frames->end(); ++it, ++i) { - it->num = i; - it->pc = cork_frames[i].absolute_pc; - it->sp = cork_frames[i].stack_top; - it->stack_size = cork_frames[i].stack_size; - it->map = NULL; - it->func_offset = 0; - } - } -} - -//------------------------------------------------------------------------- -// CorkscrewPtrace functions. -//------------------------------------------------------------------------- -CorkscrewPtrace::CorkscrewPtrace() : ptrace_context_(NULL) { -} - -CorkscrewPtrace::~CorkscrewPtrace() { - if (ptrace_context_) { - free_ptrace_context(ptrace_context_); - ptrace_context_ = NULL; - } -} - -bool CorkscrewPtrace::Unwind(size_t num_ignore_frames) { - ptrace_context_ = load_ptrace_context(Tid()); - - backtrace_frame_t frames[MAX_BACKTRACE_FRAMES]; - ssize_t num_frames = unwind_backtrace_ptrace( - Tid(), ptrace_context_, frames, num_ignore_frames, MAX_BACKTRACE_FRAMES); - - return GenerateFrameData(frames, num_frames); -} - -std::string CorkscrewPtrace::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) { - // Get information about a different process. - const map_info_t* map_info; - const symbol_t* symbol; - find_symbol_ptrace(ptrace_context_, pc, &map_info, &symbol); - char* symbol_name = NULL; - if (symbol) { - if (map_info) { - *offset = pc - map_info->start - symbol->start; - } - symbol_name = symbol->name; - return symbol_name; - } - - return ""; -} - -//------------------------------------------------------------------------- -// C++ object creation functions. -//------------------------------------------------------------------------- -Backtrace* CreateCurrentObj(BacktraceMap* map) { - return new BacktraceCurrent(new CorkscrewCurrent(), map); -} - -Backtrace* CreatePtraceObj(pid_t pid, pid_t tid, BacktraceMap* map) { - return new BacktracePtrace(new CorkscrewPtrace(), pid, tid, map); -} - -Backtrace* CreateThreadObj(pid_t tid, BacktraceMap* map) { - CorkscrewThread* thread_obj = new CorkscrewThread(); - return new BacktraceThread(thread_obj, thread_obj, tid, map); -} - -//------------------------------------------------------------------------- -// BacktraceMap create function. -//------------------------------------------------------------------------- -BacktraceMap* BacktraceMap::Create(pid_t pid) { - BacktraceMap* map = new CorkscrewMap(pid); - if (!map->Build()) { - delete map; - return NULL; - } - return map; -} diff --git a/libbacktrace/Corkscrew.h b/libbacktrace/Corkscrew.h deleted file mode 100644 index 1633398..0000000 --- a/libbacktrace/Corkscrew.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2013 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. - */ - -#ifndef _LIBBACKTRACE_CORKSCREW_H -#define _LIBBACKTRACE_CORKSCREW_H - -#include <inttypes.h> - -#include <string> - -#include <backtrace/Backtrace.h> -#include <backtrace/BacktraceMap.h> - -#include <corkscrew/backtrace.h> - -#include "BacktraceImpl.h" -#include "BacktraceThread.h" - -class CorkscrewMap : public BacktraceMap { -public: - CorkscrewMap(pid_t pid); - virtual ~CorkscrewMap(); - - virtual bool Build(); - - map_info_t* GetMapInfo() { return map_info_; } - -private: - map_info_t* map_info_; -}; - -class CorkscrewCommon : public BacktraceImpl { -public: - bool GenerateFrameData(backtrace_frame_t* cork_frames, ssize_t num_frames); -}; - -class CorkscrewCurrent : public CorkscrewCommon { -public: - CorkscrewCurrent(); - virtual ~CorkscrewCurrent(); - - virtual bool Unwind(size_t num_ignore_threads); - - virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset); -}; - -class CorkscrewThread : public CorkscrewCurrent, public BacktraceThreadInterface { -public: - CorkscrewThread(); - virtual ~CorkscrewThread(); - - virtual void ThreadUnwind( - siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames); -}; - -class CorkscrewPtrace : public CorkscrewCommon { -public: - CorkscrewPtrace(); - virtual ~CorkscrewPtrace(); - - virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset); - - virtual bool Unwind(size_t num_ignore_threads); - -private: - ptrace_context_t* ptrace_context_; -}; - -#endif // _LIBBACKTRACE_CORKSCREW_H diff --git a/libbacktrace/GetPss.cpp b/libbacktrace/GetPss.cpp new file mode 100644 index 0000000..442383b --- /dev/null +++ b/libbacktrace/GetPss.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2014 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 <assert.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <sys/types.h> +#include <unistd.h> +#include <fcntl.h> + +// This is an extremely simplified version of libpagemap. + +#define _BITS(x, offset, bits) (((x) >> offset) & ((1LL << (bits)) - 1)) + +#define PAGEMAP_PRESENT(x) (_BITS(x, 63, 1)) +#define PAGEMAP_SWAPPED(x) (_BITS(x, 62, 1)) +#define PAGEMAP_SHIFT(x) (_BITS(x, 55, 6)) +#define PAGEMAP_PFN(x) (_BITS(x, 0, 55)) +#define PAGEMAP_SWAP_OFFSET(x) (_BITS(x, 5, 50)) +#define PAGEMAP_SWAP_TYPE(x) (_BITS(x, 0, 5)) + +static bool ReadData(int fd, unsigned long place, uint64_t *data) { + if (lseek(fd, place * sizeof(uint64_t), SEEK_SET) < 0) { + return false; + } + if (read(fd, (void*)data, sizeof(uint64_t)) != (ssize_t)sizeof(uint64_t)) { + return false; + } + return true; +} + +size_t GetPssBytes() { + FILE* maps = fopen("/proc/self/maps", "r"); + assert(maps != NULL); + + int pagecount_fd = open("/proc/kpagecount", O_RDONLY); + assert(pagecount_fd >= 0); + + int pagemap_fd = open("/proc/self/pagemap", O_RDONLY); + assert(pagemap_fd >= 0); + + char line[4096]; + size_t total_pss = 0; + int pagesize = getpagesize(); + while (fgets(line, sizeof(line), maps)) { + uintptr_t start, end; + if (sscanf(line, "%" SCNxPTR "-%" SCNxPTR " ", &start, &end) != 2) { + total_pss = 0; + break; + } + for (size_t page = start/pagesize; page < end/pagesize; page++) { + uint64_t data; + if (ReadData(pagemap_fd, page, &data)) { + if (PAGEMAP_PRESENT(data) && !PAGEMAP_SWAPPED(data)) { + uint64_t count; + if (ReadData(pagecount_fd, PAGEMAP_PFN(data), &count)) { + total_pss += (count >= 1) ? pagesize / count : 0; + } + } + } + } + } + + fclose(maps); + + close(pagecount_fd); + close(pagemap_fd); + + return total_pss; +} diff --git a/libnl_2/cache.c b/libbacktrace/GetPss.h index c21974d..787c33d 100644 --- a/libnl_2/cache.c +++ b/libbacktrace/GetPss.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 The Android Open Source Project + * Copyright (C) 2014 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. @@ -14,24 +14,9 @@ * limitations under the License. */ -/* NOTICE: This is a clean room re-implementation of libnl */ - -#include "netlink/cache.h" -#include "netlink/object.h" - -void nl_cache_free(struct nl_cache *cache) -{ - -} - -void nl_cache_clear(struct nl_cache *cache) -{ - -} - -void nl_cache_remove(struct nl_object *obj) -{ - -} +#ifndef _LIBBACKTRACE_GET_PSS_H +#define _LIBBACKTRACE_GET_PSS_H +size_t GetPssBytes(); +#endif // _LIBBACKTRACE_GET_PSS_H diff --git a/libbacktrace/UnwindCurrent.cpp b/libbacktrace/UnwindCurrent.cpp index 17b71b9..b176aaf 100644..100755 --- a/libbacktrace/UnwindCurrent.cpp +++ b/libbacktrace/UnwindCurrent.cpp @@ -14,9 +14,8 @@ * limitations under the License. */ -#define LOG_TAG "libbacktrace" - #include <sys/types.h> +#include <ucontext.h> #include <backtrace/Backtrace.h> #include <backtrace/BacktraceMap.h> @@ -24,30 +23,11 @@ #define UNW_LOCAL_ONLY #include <libunwind.h> +#include "BacktraceLog.h" +#include "BacktraceThread.h" #include "UnwindCurrent.h" #include "UnwindMap.h" -// Define the ucontext_t structures needed for each supported arch. -#if defined(__arm__) - // The current version of the <signal.h> doesn't define ucontext_t. - #include <asm/sigcontext.h> // Ensure 'struct sigcontext' is defined. - - // Machine context at the time a signal was raised. - typedef struct ucontext { - uint32_t uc_flags; - struct ucontext* uc_link; - stack_t uc_stack; - struct sigcontext uc_mcontext; - uint32_t uc_sigmask; - } ucontext_t; -#elif defined(__i386__) - #include <asm/sigcontext.h> - #include <asm/ucontext.h> - typedef struct ucontext ucontext_t; -#elif !defined(__mips__) && !defined(__aarch64__) - #error Unsupported architecture. -#endif - //------------------------------------------------------------------------- // UnwindCurrent functions. //------------------------------------------------------------------------- @@ -57,13 +37,43 @@ UnwindCurrent::UnwindCurrent() { UnwindCurrent::~UnwindCurrent() { } -bool UnwindCurrent::Unwind(size_t num_ignore_frames) { - int ret = unw_getcontext(&context_); - if (ret < 0) { - BACK_LOGW("unw_getcontext failed %d", ret); - return false; +bool UnwindCurrent::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) { + if (!ucontext) { + int ret = unw_getcontext(&context_); + if (ret < 0) { + BACK_LOGW("unw_getcontext failed %d", ret); + return false; + } } - return UnwindFromContext(num_ignore_frames, true); + else { + GetUnwContextFromUcontext(ucontext); + } + return UnwindFromContext(num_ignore_frames, false); +} + +void UnwindCurrent::GetUnwContextFromUcontext(const ucontext_t* ucontext) { + unw_tdep_context_t* unw_context = reinterpret_cast<unw_tdep_context_t*>(&context_); + +#if defined(__arm__) + unw_context->regs[0] = ucontext->uc_mcontext.arm_r0; + unw_context->regs[1] = ucontext->uc_mcontext.arm_r1; + unw_context->regs[2] = ucontext->uc_mcontext.arm_r2; + unw_context->regs[3] = ucontext->uc_mcontext.arm_r3; + unw_context->regs[4] = ucontext->uc_mcontext.arm_r4; + unw_context->regs[5] = ucontext->uc_mcontext.arm_r5; + unw_context->regs[6] = ucontext->uc_mcontext.arm_r6; + unw_context->regs[7] = ucontext->uc_mcontext.arm_r7; + unw_context->regs[8] = ucontext->uc_mcontext.arm_r8; + unw_context->regs[9] = ucontext->uc_mcontext.arm_r9; + unw_context->regs[10] = ucontext->uc_mcontext.arm_r10; + unw_context->regs[11] = ucontext->uc_mcontext.arm_fp; + unw_context->regs[12] = ucontext->uc_mcontext.arm_ip; + unw_context->regs[13] = ucontext->uc_mcontext.arm_sp; + unw_context->regs[14] = ucontext->uc_mcontext.arm_lr; + unw_context->regs[15] = ucontext->uc_mcontext.arm_pc; +#else + unw_context->uc_mcontext = ucontext->uc_mcontext; +#endif } std::string UnwindCurrent::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) { @@ -78,12 +88,14 @@ std::string UnwindCurrent::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) { return ""; } -bool UnwindCurrent::UnwindFromContext(size_t num_ignore_frames, bool resolve) { +bool UnwindCurrent::UnwindFromContext(size_t num_ignore_frames, bool within_handler) { // The cursor structure is pretty large, do not put it on the stack. unw_cursor_t* cursor = new unw_cursor_t; int ret = unw_init_local(cursor, &context_); if (ret < 0) { - BACK_LOGW("unw_init_local failed %d", ret); + if (!within_handler) { + BACK_LOGW("unw_init_local failed %d", ret); + } delete cursor; return false; } @@ -95,13 +107,17 @@ bool UnwindCurrent::UnwindFromContext(size_t num_ignore_frames, bool resolve) { unw_word_t pc; ret = unw_get_reg(cursor, UNW_REG_IP, &pc); if (ret < 0) { - BACK_LOGW("Failed to read IP %d", ret); + if (!within_handler) { + BACK_LOGW("Failed to read IP %d", ret); + } break; } unw_word_t sp; ret = unw_get_reg(cursor, UNW_REG_SP, &sp); if (ret < 0) { - BACK_LOGW("Failed to read SP %d", ret); + if (!within_handler) { + BACK_LOGW("Failed to read SP %d", ret); + } break; } @@ -119,7 +135,7 @@ bool UnwindCurrent::UnwindFromContext(size_t num_ignore_frames, bool resolve) { prev->stack_size = frame->sp - prev->sp; } - if (resolve) { + if (!within_handler) { frame->func_name = GetFunctionName(frame->pc, &frame->func_offset); frame->map = FindMap(frame->pc); } else { @@ -137,47 +153,6 @@ bool UnwindCurrent::UnwindFromContext(size_t num_ignore_frames, bool resolve) { return true; } -void UnwindCurrent::ExtractContext(void* sigcontext) { - unw_tdep_context_t* context = reinterpret_cast<unw_tdep_context_t*>(&context_); - const ucontext_t* uc = reinterpret_cast<const ucontext_t*>(sigcontext); - -#if defined(__arm__) - context->regs[0] = uc->uc_mcontext.arm_r0; - context->regs[1] = uc->uc_mcontext.arm_r1; - context->regs[2] = uc->uc_mcontext.arm_r2; - context->regs[3] = uc->uc_mcontext.arm_r3; - context->regs[4] = uc->uc_mcontext.arm_r4; - context->regs[5] = uc->uc_mcontext.arm_r5; - context->regs[6] = uc->uc_mcontext.arm_r6; - context->regs[7] = uc->uc_mcontext.arm_r7; - context->regs[8] = uc->uc_mcontext.arm_r8; - context->regs[9] = uc->uc_mcontext.arm_r9; - context->regs[10] = uc->uc_mcontext.arm_r10; - context->regs[11] = uc->uc_mcontext.arm_fp; - context->regs[12] = uc->uc_mcontext.arm_ip; - context->regs[13] = uc->uc_mcontext.arm_sp; - context->regs[14] = uc->uc_mcontext.arm_lr; - context->regs[15] = uc->uc_mcontext.arm_pc; -#elif defined(__mips__) || defined(__i386__) - context->uc_mcontext = uc->uc_mcontext; -#endif -} - -//------------------------------------------------------------------------- -// UnwindThread functions. -//------------------------------------------------------------------------- -UnwindThread::UnwindThread() { -} - -UnwindThread::~UnwindThread() { -} - -void UnwindThread::ThreadUnwind( - siginfo_t* /*siginfo*/, void* sigcontext, size_t num_ignore_frames) { - ExtractContext(sigcontext); - UnwindFromContext(num_ignore_frames, false); -} - //------------------------------------------------------------------------- // C++ object creation function. //------------------------------------------------------------------------- @@ -186,6 +161,5 @@ Backtrace* CreateCurrentObj(BacktraceMap* map) { } Backtrace* CreateThreadObj(pid_t tid, BacktraceMap* map) { - UnwindThread* thread_obj = new UnwindThread(); - return new BacktraceThread(thread_obj, thread_obj, tid, map); + return new BacktraceThread(new UnwindCurrent(), tid, map); } diff --git a/libbacktrace/UnwindCurrent.h b/libbacktrace/UnwindCurrent.h index acce110..2375e6e 100644 --- a/libbacktrace/UnwindCurrent.h +++ b/libbacktrace/UnwindCurrent.h @@ -20,7 +20,6 @@ #include <string> #include "BacktraceImpl.h" -#include "BacktraceThread.h" #define UNW_LOCAL_ONLY #include <libunwind.h> @@ -30,25 +29,16 @@ public: UnwindCurrent(); virtual ~UnwindCurrent(); - virtual bool Unwind(size_t num_ignore_frames); + virtual bool Unwind(size_t num_ignore_frames, ucontext_t* ucontext); virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset); - bool UnwindFromContext(size_t num_ignore_frames, bool resolve); + bool UnwindFromContext(size_t num_ignore_frames, bool within_handler); - void ExtractContext(void* sigcontext); + void GetUnwContextFromUcontext(const ucontext_t* context); protected: unw_context_t context_; }; -class UnwindThread : public UnwindCurrent, public BacktraceThreadInterface { -public: - UnwindThread(); - virtual ~UnwindThread(); - - virtual void ThreadUnwind( - siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames); -}; - #endif // _LIBBACKTRACE_UNWIND_CURRENT_H diff --git a/libbacktrace/UnwindMap.cpp b/libbacktrace/UnwindMap.cpp index 03bb192..4f9831b 100644 --- a/libbacktrace/UnwindMap.cpp +++ b/libbacktrace/UnwindMap.cpp @@ -14,9 +14,8 @@ * limitations under the License. */ -#define LOG_TAG "libbacktrace" - #include <pthread.h> +#include <stdlib.h> #include <sys/types.h> #include <unistd.h> @@ -24,6 +23,7 @@ #include <libunwind.h> +#include "BacktraceLog.h" #include "UnwindMap.h" //------------------------------------------------------------------------- @@ -32,57 +32,21 @@ // only update the local address space once, and keep a reference count // of maps using the same map cursor. //------------------------------------------------------------------------- -static pthread_mutex_t g_map_mutex = PTHREAD_MUTEX_INITIALIZER; -static unw_map_cursor_t g_map_cursor; -static int g_map_references = 0; - UnwindMap::UnwindMap(pid_t pid) : BacktraceMap(pid) { - map_cursor_.map_list = NULL; } UnwindMap::~UnwindMap() { - if (pid_ == getpid()) { - pthread_mutex_lock(&g_map_mutex); - if (--g_map_references == 0) { - // Clear the local address space map. - unw_map_set(unw_local_addr_space, NULL); - unw_map_cursor_destroy(&map_cursor_); - } - pthread_mutex_unlock(&g_map_mutex); - } else { - unw_map_cursor_destroy(&map_cursor_); - } + unw_map_cursor_destroy(&map_cursor_); + unw_map_cursor_clear(&map_cursor_); } -bool UnwindMap::Build() { - bool return_value = true; - if (pid_ == getpid()) { - pthread_mutex_lock(&g_map_mutex); - if (g_map_references == 0) { - return_value = (unw_map_cursor_create(&map_cursor_, pid_) == 0); - if (return_value) { - // Set the local address space to this cursor map. - unw_map_set(unw_local_addr_space, &map_cursor_); - g_map_references = 1; - g_map_cursor = map_cursor_; - } - } else { - g_map_references++; - map_cursor_ = g_map_cursor; - } - pthread_mutex_unlock(&g_map_mutex); - } else { - return_value = (unw_map_cursor_create(&map_cursor_, pid_) == 0); - } - - if (!return_value) - return false; - +bool UnwindMap::GenerateMap() { // Use the map_cursor information to construct the BacktraceMap data // rather than reparsing /proc/self/maps. unw_map_cursor_reset(&map_cursor_); + unw_map_t unw_map; - while (unw_map_cursor_get(&map_cursor_, &unw_map)) { + while (unw_map_cursor_get_next(&map_cursor_, &unw_map)) { backtrace_map_t map; map.start = unw_map.start; @@ -97,11 +61,82 @@ bool UnwindMap::Build() { return true; } +bool UnwindMap::Build() { + return (unw_map_cursor_create(&map_cursor_, pid_) == 0) && GenerateMap(); +} + +UnwindMapLocal::UnwindMapLocal() : UnwindMap(getpid()), map_created_(false) { +} + +UnwindMapLocal::~UnwindMapLocal() { + if (map_created_) { + unw_map_local_destroy(); + unw_map_cursor_clear(&map_cursor_); + } +} + +bool UnwindMapLocal::GenerateMap() { + // It's possible for the map to be regenerated while this loop is occurring. + // If that happens, get the map again, but only try at most three times + // before giving up. + for (int i = 0; i < 3; i++) { + maps_.clear(); + + unw_map_local_cursor_get(&map_cursor_); + + unw_map_t unw_map; + int ret; + while ((ret = unw_map_local_cursor_get_next(&map_cursor_, &unw_map)) > 0) { + backtrace_map_t map; + + map.start = unw_map.start; + map.end = unw_map.end; + map.flags = unw_map.flags; + map.name = unw_map.path; + + free(unw_map.path); + + // The maps are in descending order, but we want them in ascending order. + maps_.push_front(map); + } + // Check to see if the map changed while getting the data. + if (ret != -UNW_EINVAL) { + return true; + } + } + + BACK_LOGW("Unable to generate the map."); + return false; +} + +bool UnwindMapLocal::Build() { + return (map_created_ = (unw_map_local_create() == 0)) && GenerateMap();; +} + +const backtrace_map_t* UnwindMapLocal::Find(uintptr_t addr) { + const backtrace_map_t* map = BacktraceMap::Find(addr); + if (!map) { + // Check to see if the underlying map changed and regenerate the map + // if it did. + if (unw_map_local_cursor_valid(&map_cursor_) < 0) { + if (GenerateMap()) { + map = BacktraceMap::Find(addr); + } + } + } + return map; +} + //------------------------------------------------------------------------- // BacktraceMap create function. //------------------------------------------------------------------------- BacktraceMap* BacktraceMap::Create(pid_t pid) { - BacktraceMap* map = new UnwindMap(pid); + BacktraceMap* map; + if (pid == getpid()) { + map = new UnwindMapLocal(); + } else { + map = new UnwindMap(pid); + } if (!map->Build()) { delete map; return NULL; diff --git a/libbacktrace/UnwindMap.h b/libbacktrace/UnwindMap.h index 5a874e8..2fdb29f 100644 --- a/libbacktrace/UnwindMap.h +++ b/libbacktrace/UnwindMap.h @@ -32,8 +32,25 @@ public: unw_map_cursor_t* GetMapCursor() { return &map_cursor_; } -private: +protected: + virtual bool GenerateMap(); + unw_map_cursor_t map_cursor_; }; +class UnwindMapLocal : public UnwindMap { +public: + UnwindMapLocal(); + virtual ~UnwindMapLocal(); + + virtual bool Build(); + + virtual const backtrace_map_t* Find(uintptr_t addr); + +protected: + virtual bool GenerateMap(); + + bool map_created_; +}; + #endif // _LIBBACKTRACE_UNWIND_MAP_H diff --git a/libbacktrace/UnwindPtrace.cpp b/libbacktrace/UnwindPtrace.cpp index 732dae8..7ba8775 100644 --- a/libbacktrace/UnwindPtrace.cpp +++ b/libbacktrace/UnwindPtrace.cpp @@ -14,17 +14,17 @@ * limitations under the License. */ -#define LOG_TAG "libbacktrace" - #include <backtrace/Backtrace.h> #include <backtrace/BacktraceMap.h> #include <sys/types.h> #include <string.h> +#include <ucontext.h> #include <libunwind.h> #include <libunwind-ptrace.h> +#include "BacktraceLog.h" #include "UnwindMap.h" #include "UnwindPtrace.h" @@ -46,7 +46,12 @@ UnwindPtrace::~UnwindPtrace() { } } -bool UnwindPtrace::Unwind(size_t num_ignore_frames) { +bool UnwindPtrace::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) { + if (ucontext) { + BACK_LOGW("Unwinding from a specified context not supported yet."); + return false; + } + addr_space_ = unw_create_addr_space(&_UPT_accessors, 0); if (!addr_space_) { BACK_LOGW("unw_create_addr_space failed."); diff --git a/libbacktrace/UnwindPtrace.h b/libbacktrace/UnwindPtrace.h index 1e82117..2fb7967 100644 --- a/libbacktrace/UnwindPtrace.h +++ b/libbacktrace/UnwindPtrace.h @@ -28,7 +28,7 @@ public: UnwindPtrace(); virtual ~UnwindPtrace(); - virtual bool Unwind(size_t num_ignore_frames); + virtual bool Unwind(size_t num_ignore_frames, ucontext_t* ucontext); virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset); diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp index 23eaf92..ed6b211 100644 --- a/libbacktrace/backtrace_test.cpp +++ b/libbacktrace/backtrace_test.cpp @@ -16,9 +16,10 @@ #include <dirent.h> #include <errno.h> +#include <inttypes.h> #include <pthread.h> #include <signal.h> -#include <stdbool.h> +#include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -32,9 +33,13 @@ #include <backtrace/BacktraceMap.h> #include <UniquePtr.h> +// For the THREAD_SIGNAL definition. +#include "BacktraceThread.h" + #include <cutils/atomic.h> #include <gtest/gtest.h> +#include <algorithm> #include <vector> #include "thread_utils.h" @@ -287,7 +292,7 @@ TEST(libbacktrace, ptrace_trace) { pid_t pid; if ((pid = fork()) == 0) { ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0); - exit(1); + _exit(1); } VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyLevelBacktrace, VerifyLevelDump); @@ -300,7 +305,7 @@ TEST(libbacktrace, ptrace_trace_shared_map) { pid_t pid; if ((pid = fork()) == 0) { ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0); - exit(1); + _exit(1); } VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, true, ReadyLevelBacktrace, VerifyLevelDump); @@ -314,7 +319,7 @@ TEST(libbacktrace, ptrace_max_trace) { pid_t pid; if ((pid = fork()) == 0) { ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, NULL, NULL), 0); - exit(1); + _exit(1); } VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyMaxBacktrace, VerifyMaxDump); @@ -339,7 +344,7 @@ TEST(libbacktrace, ptrace_ignore_frames) { pid_t pid; if ((pid = fork()) == 0) { ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0); - exit(1); + _exit(1); } VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyLevelBacktrace, VerifyProcessIgnoreFrames); @@ -384,7 +389,7 @@ TEST(libbacktrace, ptrace_threads) { ASSERT_TRUE(pthread_create(&thread, &attr, PtraceThreadLevelRun, NULL) == 0); } ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0); - exit(1); + _exit(1); } // Check to see that all of the threads are running before unwinding. @@ -458,9 +463,15 @@ TEST(libbacktrace, thread_level_trace) { // Wait up to 2 seconds for the tid to be set. ASSERT_TRUE(WaitForNonZero(&thread_data.state, 2)); + // Make sure that the thread signal used is not visible when compiled for + // the target. +#if !defined(__GLIBC__) + ASSERT_LT(THREAD_SIGNAL, SIGRTMIN); +#endif + // Save the current signal action and make sure it is restored afterwards. struct sigaction cur_action; - ASSERT_TRUE(sigaction(SIGURG, NULL, &cur_action) == 0); + ASSERT_TRUE(sigaction(THREAD_SIGNAL, NULL, &cur_action) == 0); UniquePtr<Backtrace> backtrace(Backtrace::Create(getpid(), thread_data.tid)); ASSERT_TRUE(backtrace.get() != NULL); @@ -473,7 +484,7 @@ TEST(libbacktrace, thread_level_trace) { // Verify that the old action was restored. struct sigaction new_action; - ASSERT_TRUE(sigaction(SIGURG, NULL, &new_action) == 0); + ASSERT_TRUE(sigaction(THREAD_SIGNAL, NULL, &new_action) == 0); EXPECT_EQ(cur_action.sa_sigaction, new_action.sa_sigaction); EXPECT_EQ(cur_action.sa_flags, new_action.sa_flags); } @@ -604,6 +615,49 @@ TEST(libbacktrace, thread_multiple_dump) { } } +TEST(libbacktrace, thread_multiple_dump_same_thread) { + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + thread_t runner; + runner.tid = 0; + runner.state = 0; + ASSERT_TRUE(pthread_create(&runner.threadId, &attr, ThreadMaxRun, &runner) == 0); + + // Wait for tids to be set. + ASSERT_TRUE(WaitForNonZero(&runner.state, 10)); + + // Start all of the dumpers at once, they will spin until they are signalled + // to begin their dump run. + int32_t dump_now = 0; + // Dump the same thread NUM_THREADS simultaneously. + std::vector<dump_thread_t> dumpers(NUM_THREADS); + for (size_t i = 0; i < NUM_THREADS; i++) { + dumpers[i].thread.tid = runner.tid; + dumpers[i].thread.state = 0; + dumpers[i].done = 0; + dumpers[i].now = &dump_now; + + ASSERT_TRUE(pthread_create(&dumpers[i].thread.threadId, &attr, ThreadDump, &dumpers[i]) == 0); + } + + // Start all of the dumpers going at once. + android_atomic_acquire_store(1, &dump_now); + + for (size_t i = 0; i < NUM_THREADS; i++) { + ASSERT_TRUE(WaitForNonZero(&dumpers[i].done, 100)); + + ASSERT_TRUE(dumpers[i].backtrace != NULL); + VerifyMaxDump(dumpers[i].backtrace); + + delete dumpers[i].backtrace; + dumpers[i].backtrace = NULL; + } + + // Tell the runner thread to exit its infinite loop. + android_atomic_acquire_store(0, &runner.state); +} + // This test is for UnwindMaps that should share the same map cursor when // multiple maps are created for the current process at the same time. TEST(libbacktrace, simultaneous_maps) { @@ -693,3 +747,136 @@ TEST(libbacktrace, format_test) { #endif backtrace->FormatFrameData(&frame)); } + +struct map_test_t { + uintptr_t start; + uintptr_t end; +}; + +bool map_sort(map_test_t i, map_test_t j) { + return i.start < j.start; +} + +static void VerifyMap(pid_t pid) { + char buffer[4096]; + snprintf(buffer, sizeof(buffer), "/proc/%d/maps", pid); + + FILE* map_file = fopen(buffer, "r"); + ASSERT_TRUE(map_file != NULL); + std::vector<map_test_t> test_maps; + while (fgets(buffer, sizeof(buffer), map_file)) { + map_test_t map; + ASSERT_EQ(2, sscanf(buffer, "%" SCNxPTR "-%" SCNxPTR " ", &map.start, &map.end)); + test_maps.push_back(map); + } + fclose(map_file); + std::sort(test_maps.begin(), test_maps.end(), map_sort); + + UniquePtr<BacktraceMap> map(BacktraceMap::Create(pid)); + + // Basic test that verifies that the map is in the expected order. + std::vector<map_test_t>::const_iterator test_it = test_maps.begin(); + for (BacktraceMap::const_iterator it = map->begin(); it != map->end(); ++it) { + ASSERT_TRUE(test_it != test_maps.end()); + ASSERT_EQ(test_it->start, it->start); + ASSERT_EQ(test_it->end, it->end); + ++test_it; + } + ASSERT_TRUE(test_it == test_maps.end()); +} + +TEST(libbacktrace, verify_map_remote) { + pid_t pid; + + if ((pid = fork()) == 0) { + while (true) { + } + _exit(0); + } + ASSERT_LT(0, pid); + + ASSERT_TRUE(ptrace(PTRACE_ATTACH, pid, 0, 0) == 0); + + // Wait for the process to get to a stopping point. + WaitForStop(pid); + + // The maps should match exactly since the forked process has been paused. + VerifyMap(pid); + + ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0); + + kill(pid, SIGKILL); + ASSERT_EQ(waitpid(pid, NULL, 0), pid); +} + +#if defined(ENABLE_PSS_TESTS) +#include "GetPss.h" + +#define MAX_LEAK_BYTES 32*1024UL + +static void CheckForLeak(pid_t pid, pid_t tid) { + // Do a few runs to get the PSS stable. + for (size_t i = 0; i < 100; i++) { + Backtrace* backtrace = Backtrace::Create(pid, tid); + ASSERT_TRUE(backtrace != NULL); + ASSERT_TRUE(backtrace->Unwind(0)); + delete backtrace; + } + size_t stable_pss = GetPssBytes(); + + // Loop enough that even a small leak should be detectable. + for (size_t i = 0; i < 4096; i++) { + Backtrace* backtrace = Backtrace::Create(pid, tid); + ASSERT_TRUE(backtrace != NULL); + ASSERT_TRUE(backtrace->Unwind(0)); + delete backtrace; + } + size_t new_pss = GetPssBytes(); + size_t abs_diff = (new_pss > stable_pss) ? new_pss - stable_pss : stable_pss - new_pss; + // As long as the new pss is within a certain amount, consider everything okay. + ASSERT_LE(abs_diff, MAX_LEAK_BYTES); +} + +TEST(libbacktrace, check_for_leak_local) { + CheckForLeak(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD); +} + +TEST(libbacktrace, check_for_leak_local_thread) { + thread_t thread_data = { 0, 0, 0 }; + pthread_t thread; + ASSERT_TRUE(pthread_create(&thread, NULL, ThreadLevelRun, &thread_data) == 0); + + // Wait up to 2 seconds for the tid to be set. + ASSERT_TRUE(WaitForNonZero(&thread_data.state, 2)); + + CheckForLeak(BACKTRACE_CURRENT_PROCESS, thread_data.tid); + + // Tell the thread to exit its infinite loop. + android_atomic_acquire_store(0, &thread_data.state); + + ASSERT_TRUE(pthread_join(thread, NULL) == 0); +} + +TEST(libbacktrace, check_for_leak_remote) { + pid_t pid; + + if ((pid = fork()) == 0) { + while (true) { + } + _exit(0); + } + ASSERT_LT(0, pid); + + ASSERT_TRUE(ptrace(PTRACE_ATTACH, pid, 0, 0) == 0); + + // Wait for the process to get to a stopping point. + WaitForStop(pid); + + CheckForLeak(pid, BACKTRACE_CURRENT_THREAD); + + ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0); + + kill(pid, SIGKILL); + ASSERT_EQ(waitpid(pid, NULL, 0), pid); +} +#endif diff --git a/libcorkscrew/Android.mk b/libcorkscrew/Android.mk deleted file mode 100644 index e275317..0000000 --- a/libcorkscrew/Android.mk +++ /dev/null @@ -1,98 +0,0 @@ -# Copyright (C) 2011 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. - -LOCAL_PATH:= $(call my-dir) - -generic_src_files := \ - backtrace.c \ - backtrace-helper.c \ - demangle.c \ - map_info.c \ - ptrace.c \ - symbol_table.c - -arm_src_files := \ - arch-arm/backtrace-arm.c \ - arch-arm/ptrace-arm.c - -x86_src_files := \ - arch-x86/backtrace-x86.c \ - arch-x86/ptrace-x86.c - -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := $(generic_src_files) - -ifeq ($(TARGET_ARCH),arm) -LOCAL_SRC_FILES += $(arm_src_files) -LOCAL_CFLAGS += -DCORKSCREW_HAVE_ARCH -endif -ifeq ($(TARGET_ARCH),x86) -LOCAL_SRC_FILES += $(x86_src_files) -LOCAL_CFLAGS += -DCORKSCREW_HAVE_ARCH -endif -ifeq ($(TARGET_ARCH),mips) -LOCAL_SRC_FILES += \ - arch-mips/backtrace-mips.c \ - arch-mips/ptrace-mips.c -LOCAL_CFLAGS += -DCORKSCREW_HAVE_ARCH -endif - -LOCAL_SHARED_LIBRARIES += libdl libcutils liblog libgccdemangle - -LOCAL_CFLAGS += -std=gnu99 -Werror -Wno-unused-parameter -LOCAL_MODULE := libcorkscrew -LOCAL_MODULE_TAGS := optional - -include $(BUILD_SHARED_LIBRARY) - -# Build test. -include $(CLEAR_VARS) -LOCAL_SRC_FILES := test.cpp -LOCAL_CFLAGS += -Werror -fno-inline-small-functions -LOCAL_SHARED_LIBRARIES := libcorkscrew -LOCAL_MODULE := libcorkscrew_test -LOCAL_MODULE_TAGS := optional -include $(BUILD_EXECUTABLE) - - -# TODO: reenable darwin-x86 -# ifeq ($(HOST_ARCH),x86) -ifeq ($(HOST_OS)-$(HOST_ARCH),linux-x86) - -# Build libcorkscrew. -include $(CLEAR_VARS) -LOCAL_SRC_FILES += $(generic_src_files) $(x86_src_files) -LOCAL_CFLAGS += -DCORKSCREW_HAVE_ARCH -LOCAL_STATIC_LIBRARIES += libcutils liblog -LOCAL_LDLIBS += -ldl -ifeq ($(HOST_OS),linux) - LOCAL_SHARED_LIBRARIES += libgccdemangle # TODO: is this even needed on Linux? - LOCAL_LDLIBS += -lrt -endif -LOCAL_CFLAGS += -std=gnu99 -Werror -Wno-unused-parameter -LOCAL_MODULE := libcorkscrew -LOCAL_MODULE_TAGS := optional -include $(BUILD_HOST_SHARED_LIBRARY) - -# Build test. -include $(CLEAR_VARS) -LOCAL_SRC_FILES := test.cpp -LOCAL_CFLAGS += -Werror -LOCAL_SHARED_LIBRARIES := libcorkscrew -LOCAL_MODULE := libcorkscrew_test -LOCAL_MODULE_TAGS := optional -include $(BUILD_HOST_EXECUTABLE) - -endif # HOST_ARCH == x86 diff --git a/libcorkscrew/MODULE_LICENSE_APACHE2 b/libcorkscrew/MODULE_LICENSE_APACHE2 deleted file mode 100644 index e69de29..0000000 --- a/libcorkscrew/MODULE_LICENSE_APACHE2 +++ /dev/null diff --git a/libcorkscrew/NOTICE b/libcorkscrew/NOTICE deleted file mode 100644 index becc120..0000000 --- a/libcorkscrew/NOTICE +++ /dev/null @@ -1,190 +0,0 @@ - - Copyright (c) 2011, 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. - - 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. - - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - diff --git a/libcorkscrew/arch-arm/backtrace-arm.c b/libcorkscrew/arch-arm/backtrace-arm.c deleted file mode 100644 index 7bd0d8f..0000000 --- a/libcorkscrew/arch-arm/backtrace-arm.c +++ /dev/null @@ -1,601 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -/* - * Backtracing functions for ARM. - * - * This implementation uses the exception unwinding tables provided by - * the compiler to unwind call frames. Refer to the ARM Exception Handling ABI - * documentation (EHABI) for more details about what's going on here. - * - * An ELF binary may contain an EXIDX section that provides an index to - * the exception handling table of each function, sorted by program - * counter address. - * - * This implementation also supports unwinding other processes via ptrace(). - * In that case, the EXIDX section is found by reading the ELF section table - * structures using ptrace(). - * - * Because the tables are used for exception handling, it can happen that - * a given function will not have an exception handling table. In particular, - * exceptions are assumed to only ever be thrown at call sites. Therefore, - * by definition leaf functions will not have exception handling tables. - * This may make unwinding impossible in some cases although we can still get - * some idea of the call stack by examining the PC and LR registers. - * - * As we are only interested in backtrace information, we do not need - * to perform all of the work of unwinding such as restoring register - * state and running cleanup functions. Unwinding is performed virtually on - * an abstract machine context consisting of just the ARM core registers. - * Furthermore, we do not run generic "personality functions" because - * we may not be in a position to execute arbitrary code, especially if - * we are running in a signal handler or using ptrace()! - */ - -#define LOG_TAG "Corkscrew" -//#define LOG_NDEBUG 0 - -#include "../backtrace-arch.h" -#include "../backtrace-helper.h" -#include "../ptrace-arch.h" -#include <corkscrew/ptrace.h> - -#include <stdlib.h> -#include <signal.h> -#include <stdbool.h> -#include <limits.h> -#include <errno.h> -#include <sys/ptrace.h> -#include <elf.h> -#include <cutils/log.h> - -#if !defined(__BIONIC_HAVE_UCONTEXT_T) -/* Old versions of the Android <signal.h> didn't define ucontext_t. */ -#include <asm/sigcontext.h> /* Ensure 'struct sigcontext' is defined. */ - -/* Machine context at the time a signal was raised. */ -typedef struct ucontext { - uint32_t uc_flags; - struct ucontext* uc_link; - stack_t uc_stack; - struct sigcontext uc_mcontext; - uint32_t uc_sigmask; -} ucontext_t; -#endif /* !__BIONIC_HAVE_UCONTEXT_T */ - -/* Unwind state. */ -typedef struct { - uint32_t gregs[16]; -} unwind_state_t; - -static const int R_SP = 13; -static const int R_LR = 14; -static const int R_PC = 15; - -/* Special EXIDX value that indicates that a frame cannot be unwound. */ -static const uint32_t EXIDX_CANTUNWIND = 1; - -/* Get the EXIDX section start and size for the module that contains a - * given program counter address. - * - * When the executable is statically linked, the EXIDX section can be - * accessed by querying the values of the __exidx_start and __exidx_end - * symbols. - * - * When the executable is dynamically linked, the linker exports a function - * called dl_unwind_find_exidx that obtains the EXIDX section for a given - * absolute program counter address. - * - * Bionic exports a helpful function called __gnu_Unwind_Find_exidx that - * handles both cases, so we use that here. - */ -typedef long unsigned int* _Unwind_Ptr; -extern _Unwind_Ptr __gnu_Unwind_Find_exidx(_Unwind_Ptr pc, int *pcount); - -static uintptr_t find_exidx(uintptr_t pc, size_t* out_exidx_size) { - int count; - uintptr_t start = (uintptr_t)__gnu_Unwind_Find_exidx((_Unwind_Ptr)pc, &count); - *out_exidx_size = count; - return start; -} - -/* Transforms a 31-bit place-relative offset to an absolute address. - * We assume the most significant bit is clear. */ -static uintptr_t prel_to_absolute(uintptr_t place, uint32_t prel_offset) { - return place + (((int32_t)(prel_offset << 1)) >> 1); -} - -static uintptr_t get_exception_handler(const memory_t* memory, - const map_info_t* map_info_list, uintptr_t pc) { - if (!pc) { - ALOGV("get_exception_handler: pc is zero, no handler"); - return 0; - } - - uintptr_t exidx_start; - size_t exidx_size; - const map_info_t* mi; - if (memory->tid < 0) { - mi = NULL; - exidx_start = find_exidx(pc, &exidx_size); - } else { - mi = find_map_info(map_info_list, pc); - if (mi && mi->data) { - const map_info_data_t* data = (const map_info_data_t*)mi->data; - exidx_start = data->exidx_start; - exidx_size = data->exidx_size; - } else { - exidx_start = 0; - exidx_size = 0; - } - } - - uintptr_t handler = 0; - int32_t handler_index = -1; - if (exidx_start) { - uint32_t low = 0; - uint32_t high = exidx_size; - while (low < high) { - uint32_t index = (low + high) / 2; - uintptr_t entry = exidx_start + index * 8; - uint32_t entry_prel_pc; - ALOGV("XXX low=%u, high=%u, index=%u", low, high, index); - if (!try_get_word(memory, entry, &entry_prel_pc)) { - break; - } - uintptr_t entry_pc = prel_to_absolute(entry, entry_prel_pc); - ALOGV("XXX entry_pc=0x%08x", entry_pc); - if (pc < entry_pc) { - high = index; - continue; - } - if (index + 1 < exidx_size) { - uintptr_t next_entry = entry + 8; - uint32_t next_entry_prel_pc; - if (!try_get_word(memory, next_entry, &next_entry_prel_pc)) { - break; - } - uintptr_t next_entry_pc = prel_to_absolute(next_entry, next_entry_prel_pc); - ALOGV("XXX next_entry_pc=0x%08x", next_entry_pc); - if (pc >= next_entry_pc) { - low = index + 1; - continue; - } - } - - uintptr_t entry_handler_ptr = entry + 4; - uint32_t entry_handler; - if (!try_get_word(memory, entry_handler_ptr, &entry_handler)) { - break; - } - if (entry_handler & (1L << 31)) { - handler = entry_handler_ptr; // in-place handler data - } else if (entry_handler != EXIDX_CANTUNWIND) { - handler = prel_to_absolute(entry_handler_ptr, entry_handler); - } - handler_index = index; - break; - } - } - if (mi) { - ALOGV("get_exception_handler: pc=0x%08x, module='%s', module_start=0x%08x, " - "exidx_start=0x%08x, exidx_size=%d, handler=0x%08x, handler_index=%d", - pc, mi->name, mi->start, exidx_start, exidx_size, handler, handler_index); - } else { - ALOGV("get_exception_handler: pc=0x%08x, " - "exidx_start=0x%08x, exidx_size=%d, handler=0x%08x, handler_index=%d", - pc, exidx_start, exidx_size, handler, handler_index); - } - return handler; -} - -typedef struct { - uintptr_t ptr; - uint32_t word; -} byte_stream_t; - -static bool try_next_byte(const memory_t* memory, byte_stream_t* stream, uint8_t* out_value) { - uint8_t result; - switch (stream->ptr & 3) { - case 0: - if (!try_get_word(memory, stream->ptr, &stream->word)) { - *out_value = 0; - return false; - } - *out_value = stream->word >> 24; - break; - - case 1: - *out_value = stream->word >> 16; - break; - - case 2: - *out_value = stream->word >> 8; - break; - - default: - *out_value = stream->word; - break; - } - - ALOGV("next_byte: ptr=0x%08x, value=0x%02x", stream->ptr, *out_value); - stream->ptr += 1; - return true; -} - -static void set_reg(unwind_state_t* state, uint32_t reg, uint32_t value) { - ALOGV("set_reg: reg=%d, value=0x%08x", reg, value); - state->gregs[reg] = value; -} - -static bool try_pop_registers(const memory_t* memory, unwind_state_t* state, uint32_t mask) { - uint32_t sp = state->gregs[R_SP]; - bool sp_updated = false; - for (int i = 0; i < 16; i++) { - if (mask & (1 << i)) { - uint32_t value; - if (!try_get_word(memory, sp, &value)) { - return false; - } - if (i == R_SP) { - sp_updated = true; - } - set_reg(state, i, value); - sp += 4; - } - } - if (!sp_updated) { - set_reg(state, R_SP, sp); - } - return true; -} - -/* Executes a built-in personality routine as defined in the EHABI. - * Returns true if unwinding should continue. - * - * The data for the built-in personality routines consists of a sequence - * of unwinding instructions, followed by a sequence of scope descriptors, - * each of which has a length and offset encoded using 16-bit or 32-bit - * values. - * - * We only care about the unwinding instructions. They specify the - * operations of an abstract machine whose purpose is to transform the - * virtual register state (including the stack pointer) such that - * the call frame is unwound and the PC register points to the call site. - */ -static bool execute_personality_routine(const memory_t* memory, - unwind_state_t* state, byte_stream_t* stream, int pr_index) { - size_t size; - switch (pr_index) { - case 0: // Personality routine #0, short frame, descriptors have 16-bit scope. - size = 3; - break; - case 1: // Personality routine #1, long frame, descriptors have 16-bit scope. - case 2: { // Personality routine #2, long frame, descriptors have 32-bit scope. - uint8_t size_byte; - if (!try_next_byte(memory, stream, &size_byte)) { - return false; - } - size = (uint32_t)size_byte * sizeof(uint32_t) + 2; - break; - } - default: // Unknown personality routine. Stop here. - return false; - } - - bool pc_was_set = false; - while (size--) { - uint8_t op; - if (!try_next_byte(memory, stream, &op)) { - return false; - } - if ((op & 0xc0) == 0x00) { - // "vsp = vsp + (xxxxxx << 2) + 4" - set_reg(state, R_SP, state->gregs[R_SP] + ((op & 0x3f) << 2) + 4); - } else if ((op & 0xc0) == 0x40) { - // "vsp = vsp - (xxxxxx << 2) - 4" - set_reg(state, R_SP, state->gregs[R_SP] - ((op & 0x3f) << 2) - 4); - } else if ((op & 0xf0) == 0x80) { - uint8_t op2; - if (!(size--) || !try_next_byte(memory, stream, &op2)) { - return false; - } - uint32_t mask = (((uint32_t)op & 0x0f) << 12) | ((uint32_t)op2 << 4); - if (mask) { - // "Pop up to 12 integer registers under masks {r15-r12}, {r11-r4}" - if (!try_pop_registers(memory, state, mask)) { - return false; - } - if (mask & (1 << R_PC)) { - pc_was_set = true; - } - } else { - // "Refuse to unwind" - return false; - } - } else if ((op & 0xf0) == 0x90) { - if (op != 0x9d && op != 0x9f) { - // "Set vsp = r[nnnn]" - set_reg(state, R_SP, state->gregs[op & 0x0f]); - } else { - // "Reserved as prefix for ARM register to register moves" - // "Reserved as prefix for Intel Wireless MMX register to register moves" - return false; - } - } else if ((op & 0xf8) == 0xa0) { - // "Pop r4-r[4+nnn]" - uint32_t mask = (0x0ff0 >> (7 - (op & 0x07))) & 0x0ff0; - if (!try_pop_registers(memory, state, mask)) { - return false; - } - } else if ((op & 0xf8) == 0xa8) { - // "Pop r4-r[4+nnn], r14" - uint32_t mask = ((0x0ff0 >> (7 - (op & 0x07))) & 0x0ff0) | 0x4000; - if (!try_pop_registers(memory, state, mask)) { - return false; - } - } else if (op == 0xb0) { - // "Finish" - break; - } else if (op == 0xb1) { - uint8_t op2; - if (!(size--) || !try_next_byte(memory, stream, &op2)) { - return false; - } - if (op2 != 0x00 && (op2 & 0xf0) == 0x00) { - // "Pop integer registers under mask {r3, r2, r1, r0}" - if (!try_pop_registers(memory, state, op2)) { - return false; - } - } else { - // "Spare" - return false; - } - } else if (op == 0xb2) { - // "vsp = vsp + 0x204 + (uleb128 << 2)" - uint32_t value = 0; - uint32_t shift = 0; - uint8_t op2; - do { - if (!(size--) || !try_next_byte(memory, stream, &op2)) { - return false; - } - value |= (op2 & 0x7f) << shift; - shift += 7; - } while (op2 & 0x80); - set_reg(state, R_SP, state->gregs[R_SP] + (value << 2) + 0x204); - } else if (op == 0xb3) { - // "Pop VFP double-precision registers D[ssss]-D[ssss+cccc] saved (as if) by FSTMFDX" - uint8_t op2; - if (!(size--) || !try_next_byte(memory, stream, &op2)) { - return false; - } - set_reg(state, R_SP, state->gregs[R_SP] + (uint32_t)(op2 & 0x0f) * 8 + 12); - } else if ((op & 0xf8) == 0xb8) { - // "Pop VFP double-precision registers D[8]-D[8+nnn] saved (as if) by FSTMFDX" - set_reg(state, R_SP, state->gregs[R_SP] + (uint32_t)(op & 0x07) * 8 + 12); - } else if ((op & 0xf8) == 0xc0) { - // "Intel Wireless MMX pop wR[10]-wR[10+nnn]" - set_reg(state, R_SP, state->gregs[R_SP] + (uint32_t)(op & 0x07) * 8 + 8); - } else if (op == 0xc6) { - // "Intel Wireless MMX pop wR[ssss]-wR[ssss+cccc]" - uint8_t op2; - if (!(size--) || !try_next_byte(memory, stream, &op2)) { - return false; - } - set_reg(state, R_SP, state->gregs[R_SP] + (uint32_t)(op2 & 0x0f) * 8 + 8); - } else if (op == 0xc7) { - uint8_t op2; - if (!(size--) || !try_next_byte(memory, stream, &op2)) { - return false; - } - if (op2 != 0x00 && (op2 & 0xf0) == 0x00) { - // "Intel Wireless MMX pop wCGR registers under mask {wCGR3,2,1,0}" - set_reg(state, R_SP, state->gregs[R_SP] + __builtin_popcount(op2) * 4); - } else { - // "Spare" - return false; - } - } else if (op == 0xc8) { - // "Pop VFP double precision registers D[16+ssss]-D[16+ssss+cccc] - // saved (as if) by FSTMFD" - uint8_t op2; - if (!(size--) || !try_next_byte(memory, stream, &op2)) { - return false; - } - set_reg(state, R_SP, state->gregs[R_SP] + (uint32_t)(op2 & 0x0f) * 8 + 8); - } else if (op == 0xc9) { - // "Pop VFP double precision registers D[ssss]-D[ssss+cccc] saved (as if) by FSTMFDD" - uint8_t op2; - if (!(size--) || !try_next_byte(memory, stream, &op2)) { - return false; - } - set_reg(state, R_SP, state->gregs[R_SP] + (uint32_t)(op2 & 0x0f) * 8 + 8); - } else if ((op == 0xf8) == 0xd0) { - // "Pop VFP double-precision registers D[8]-D[8+nnn] saved (as if) by FSTMFDD" - set_reg(state, R_SP, state->gregs[R_SP] + (uint32_t)(op & 0x07) * 8 + 8); - } else { - // "Spare" - return false; - } - } - if (!pc_was_set) { - set_reg(state, R_PC, state->gregs[R_LR]); - } - return true; -} - -static bool try_get_half_word(const memory_t* memory, uint32_t pc, uint16_t* out_value) { - uint32_t word; - if (try_get_word(memory, pc & ~2, &word)) { - *out_value = pc & 2 ? word >> 16 : word & 0xffff; - return true; - } - return false; -} - -uintptr_t rewind_pc_arch(const memory_t* memory, uintptr_t pc) { - if (pc & 1) { - /* Thumb mode - need to check whether the bl(x) has long offset or not. - * Examples: - * - * arm blx in the middle of thumb: - * 187ae: 2300 movs r3, #0 - * 187b0: f7fe ee1c blx 173ec - * 187b4: 2c00 cmp r4, #0 - * - * arm bl in the middle of thumb: - * 187d8: 1c20 adds r0, r4, #0 - * 187da: f136 fd15 bl 14f208 - * 187de: 2800 cmp r0, #0 - * - * pure thumb: - * 18894: 189b adds r3, r3, r2 - * 18896: 4798 blx r3 - * 18898: b001 add sp, #4 - */ - uint16_t prev1, prev2; - if (try_get_half_word(memory, pc - 5, &prev1) - && ((prev1 & 0xf000) == 0xf000) - && try_get_half_word(memory, pc - 3, &prev2) - && ((prev2 & 0xe000) == 0xe000)) { - pc -= 4; // long offset - } else { - pc -= 2; - } - } else { - /* ARM mode, all instructions are 32bit. Yay! */ - pc -= 4; - } - return pc; -} - -static ssize_t unwind_backtrace_common(const memory_t* memory, - const map_info_t* map_info_list, - unwind_state_t* state, backtrace_frame_t* backtrace, - size_t ignore_depth, size_t max_depth) { - size_t ignored_frames = 0; - size_t returned_frames = 0; - - for (size_t index = 0; returned_frames < max_depth; index++) { - uintptr_t pc = index ? rewind_pc_arch(memory, state->gregs[R_PC]) - : state->gregs[R_PC]; - backtrace_frame_t* frame = add_backtrace_entry(pc, - backtrace, ignore_depth, max_depth, &ignored_frames, &returned_frames); - if (frame) { - frame->stack_top = state->gregs[R_SP]; - } - - uintptr_t handler = get_exception_handler(memory, map_info_list, pc); - if (!handler) { - // If there is no handler for the PC and this is the first frame, - // then the program may have branched to an invalid address. - // Try starting from the LR instead, otherwise stop unwinding. - if (index == 0 && state->gregs[R_LR] - && state->gregs[R_LR] != state->gregs[R_PC]) { - set_reg(state, R_PC, state->gregs[R_LR]); - continue; - } else { - break; - } - } - - byte_stream_t stream; - stream.ptr = handler; - uint8_t pr; - if (!try_next_byte(memory, &stream, &pr)) { - break; - } - if ((pr & 0xf0) != 0x80) { - // The first word is a place-relative pointer to a generic personality - // routine function. We don't support invoking such functions, so stop here. - break; - } - - // The first byte indicates the personality routine to execute. - // Following bytes provide instructions to the personality routine. - if (!execute_personality_routine(memory, state, &stream, pr & 0x0f)) { - break; - } - if (frame && state->gregs[R_SP] > frame->stack_top) { - frame->stack_size = state->gregs[R_SP] - frame->stack_top; - } - if (!state->gregs[R_PC]) { - break; - } - } - - // Ran out of frames that we could unwind using handlers. - // Add a final entry for the LR if it looks sane and call it good. - if (returned_frames < max_depth - && state->gregs[R_LR] - && state->gregs[R_LR] != state->gregs[R_PC] - && is_executable_map(map_info_list, state->gregs[R_LR])) { - // We don't know where the stack for this extra frame starts so we - // don't return any stack information for it. - add_backtrace_entry(rewind_pc_arch(memory, state->gregs[R_LR]), - backtrace, ignore_depth, max_depth, &ignored_frames, &returned_frames); - } - return returned_frames; -} - -ssize_t unwind_backtrace_signal_arch(siginfo_t* siginfo, void* sigcontext, - const map_info_t* map_info_list, - backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth) { - const ucontext_t* uc = (const ucontext_t*)sigcontext; - - unwind_state_t state; - - state.gregs[0] = uc->uc_mcontext.arm_r0; - state.gregs[1] = uc->uc_mcontext.arm_r1; - state.gregs[2] = uc->uc_mcontext.arm_r2; - state.gregs[3] = uc->uc_mcontext.arm_r3; - state.gregs[4] = uc->uc_mcontext.arm_r4; - state.gregs[5] = uc->uc_mcontext.arm_r5; - state.gregs[6] = uc->uc_mcontext.arm_r6; - state.gregs[7] = uc->uc_mcontext.arm_r7; - state.gregs[8] = uc->uc_mcontext.arm_r8; - state.gregs[9] = uc->uc_mcontext.arm_r9; - state.gregs[10] = uc->uc_mcontext.arm_r10; - state.gregs[11] = uc->uc_mcontext.arm_fp; - state.gregs[12] = uc->uc_mcontext.arm_ip; - state.gregs[13] = uc->uc_mcontext.arm_sp; - state.gregs[14] = uc->uc_mcontext.arm_lr; - state.gregs[15] = uc->uc_mcontext.arm_pc; - - memory_t memory; - init_memory(&memory, map_info_list); - return unwind_backtrace_common(&memory, map_info_list, &state, - backtrace, ignore_depth, max_depth); -} - -ssize_t unwind_backtrace_ptrace_arch(pid_t tid, const ptrace_context_t* context, - backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth) { - struct pt_regs regs; - if (ptrace(PTRACE_GETREGS, tid, 0, ®s)) { - return -1; - } - - unwind_state_t state; - for (int i = 0; i < 16; i++) { - state.gregs[i] = regs.uregs[i]; - } - - memory_t memory; - init_memory_ptrace(&memory, tid); - return unwind_backtrace_common(&memory, context->map_info_list, &state, - backtrace, ignore_depth, max_depth); -} diff --git a/libcorkscrew/arch-arm/ptrace-arm.c b/libcorkscrew/arch-arm/ptrace-arm.c deleted file mode 100644 index a50844e..0000000 --- a/libcorkscrew/arch-arm/ptrace-arm.c +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -#define LOG_TAG "Corkscrew" -//#define LOG_NDEBUG 0 - -#include "../ptrace-arch.h" - -#include <elf.h> -#include <cutils/log.h> - -#ifndef PT_ARM_EXIDX -#define PT_ARM_EXIDX 0x70000001 -#endif - -static void load_exidx_header(pid_t pid, map_info_t* mi, - uintptr_t* out_exidx_start, size_t* out_exidx_size) { - uint32_t elf_phoff; - uint32_t elf_phentsize_ehsize; - uint32_t elf_shentsize_phnum; - if (try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_phoff), &elf_phoff) - && try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_ehsize), - &elf_phentsize_ehsize) - && try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_phnum), - &elf_shentsize_phnum)) { - uint32_t elf_phentsize = elf_phentsize_ehsize >> 16; - uint32_t elf_phnum = elf_shentsize_phnum & 0xffff; - for (uint32_t i = 0; i < elf_phnum; i++) { - uintptr_t elf_phdr = mi->start + elf_phoff + i * elf_phentsize; - uint32_t elf_phdr_type; - if (!try_get_word_ptrace(pid, elf_phdr + offsetof(Elf32_Phdr, p_type), &elf_phdr_type)) { - break; - } - if (elf_phdr_type == PT_ARM_EXIDX) { - uint32_t elf_phdr_offset; - uint32_t elf_phdr_filesz; - if (!try_get_word_ptrace(pid, elf_phdr + offsetof(Elf32_Phdr, p_offset), - &elf_phdr_offset) - || !try_get_word_ptrace(pid, elf_phdr + offsetof(Elf32_Phdr, p_filesz), - &elf_phdr_filesz)) { - break; - } - *out_exidx_start = mi->start + elf_phdr_offset; - *out_exidx_size = elf_phdr_filesz / 8; - ALOGV("Parsed EXIDX header info for %s: start=0x%08x, size=%d", mi->name, - *out_exidx_start, *out_exidx_size); - return; - } - } - } - *out_exidx_start = 0; - *out_exidx_size = 0; -} - -void load_ptrace_map_info_data_arch(pid_t pid, map_info_t* mi, map_info_data_t* data) { - load_exidx_header(pid, mi, &data->exidx_start, &data->exidx_size); -} - -void free_ptrace_map_info_data_arch(map_info_t* mi, map_info_data_t* data) { -} diff --git a/libcorkscrew/arch-mips/backtrace-mips.c b/libcorkscrew/arch-mips/backtrace-mips.c deleted file mode 100644 index 57cb324..0000000 --- a/libcorkscrew/arch-mips/backtrace-mips.c +++ /dev/null @@ -1,936 +0,0 @@ -/* - * Copyright (C) 2012 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. - */ - -/* - * Backtracing functions for mips - */ - -#define LOG_TAG "Corkscrew" -//#define LOG_NDEBUG 0 - -#include "../backtrace-arch.h" -#include "../backtrace-helper.h" -#include "../ptrace-arch.h" -#include <corkscrew/ptrace.h> -#include "dwarf.h" - -#include <stdlib.h> -#include <signal.h> -#include <stdbool.h> -#include <limits.h> -#include <errno.h> -#include <string.h> -#include <sys/ptrace.h> -#include <cutils/log.h> - -#if defined(__BIONIC__) - -#if defined(__BIONIC_HAVE_UCONTEXT_T) - -// Bionic offers the Linux kernel headers. -#include <asm/sigcontext.h> -#include <asm/ucontext.h> -typedef struct ucontext ucontext_t; - -#else /* __BIONIC_HAVE_UCONTEXT_T */ - -/* Old versions of the Android <signal.h> didn't define ucontext_t. */ - -/* For PTRACE_GETREGS */ -typedef struct { - uint64_t regs[32]; - uint64_t lo; - uint64_t hi; - uint64_t epc; - uint64_t badvaddr; - uint64_t status; - uint64_t cause; -} user_regs_struct; - -enum { - REG_ZERO = 0, REG_AT, REG_V0, REG_V1, - REG_A0, REG_A1, REG_A2, REG_A3, - REG_T0, REG_T1, REG_T2, REG_T3, - REG_T4, REG_T5, REG_T6, REG_T7, - REG_S0, REG_S1, REG_S2, REG_S3, - REG_S4, REG_S5, REG_S6, REG_S7, - REG_T8, REG_T9, REG_K0, REG_K1, - REG_GP, REG_SP, REG_S8, REG_RA, -}; - -/* Machine context at the time a signal was raised. */ -typedef struct ucontext { - unsigned int sc_regmask; - unsigned int sc_status; - unsigned long long sc_pc; - unsigned long long sc_regs[32]; - unsigned long long sc_fpregs[32]; - unsigned int sc_acx; - unsigned int sc_fpc_csr; - unsigned int sc_fpc_eir; - unsigned int sc_used_math; - unsigned int sc_dsp; - unsigned long long sc_mdhi; - unsigned long long sc_mdlo; - unsigned long sc_hi1; - unsigned long sc_lo1; - unsigned long sc_hi2; - unsigned long sc_lo2; - unsigned long sc_hi3; - unsigned long sc_lo3; -} ucontext_t; - -#endif /* __BIONIC_HAVE_UCONTEXT_T */ -#endif - -/* Unwind state. */ -typedef struct { - uint32_t reg[DWARF_REGISTERS]; -} unwind_state_t; - -uintptr_t rewind_pc_arch(const memory_t* memory __attribute__((unused)), uintptr_t pc) { - if (pc == 0) - return pc; - if ((pc & 1) == 0) - return pc-8; /* jal/bal/jalr + branch delay slot */ - return pc; -} - -/* Read byte through 4 byte cache. Usually we read byte by byte and updating cursor. */ -static bool try_get_byte(const memory_t* memory, uintptr_t ptr, uint8_t* out_value, uint32_t* cursor) { - static uintptr_t lastptr; - static uint32_t buf; - - ptr += *cursor; - - if (ptr < lastptr || lastptr + 3 < ptr) { - lastptr = (ptr >> 2) << 2; - if (!try_get_word(memory, lastptr, &buf)) { - return false; - } - } - *out_value = (uint8_t)((buf >> ((ptr & 3) * 8)) & 0xff); - ++*cursor; - return true; -} - -/* Getting X bytes. 4 is maximum for now. */ -static bool try_get_xbytes(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint8_t bytes, uint32_t* cursor) { - uint32_t data = 0; - if (bytes > 4) { - ALOGE("can't read more than 4 bytes, trying to read %d", bytes); - return false; - } - for (int i = 0; i < bytes; i++) { - uint8_t buf; - if (!try_get_byte(memory, ptr, &buf, cursor)) { - return false; - } - data |= (uint32_t)buf << (i * 8); - } - *out_value = data; - return true; -} - -/* Reads signed/unsigned LEB128 encoded data. From 1 to 4 bytes. */ -static bool try_get_leb128(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint32_t* cursor, bool sign_extend) { - uint8_t buf = 0; - uint32_t val = 0; - uint8_t c = 0; - do { - if (!try_get_byte(memory, ptr, &buf, cursor)) { - return false; - } - val |= ((uint32_t)buf & 0x7f) << (c * 7); - c++; - } while (buf & 0x80 && (c * 7) <= 32); - if (c * 7 > 32) { - ALOGE("%s: data exceeds expected 4 bytes maximum", __FUNCTION__); - return false; - } - if (sign_extend) { - if (buf & 0x40) { - val |= ((uint32_t)-1 << (c * 7)); - } - } - *out_value = val; - return true; -} - -/* Reads signed LEB128 encoded data. From 1 to 4 bytes. */ -static bool try_get_sleb128(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint32_t* cursor) { - return try_get_leb128(memory, ptr, out_value, cursor, true); -} - -/* Reads unsigned LEB128 encoded data. From 1 to 4 bytes. */ -static bool try_get_uleb128(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint32_t* cursor) { - return try_get_leb128(memory, ptr, out_value, cursor, false); -} - -/* Getting data encoded by dwarf encodings. */ -static bool read_dwarf(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint8_t encoding, uint32_t* cursor) { - uint32_t data = 0; - bool issigned = true; - uintptr_t addr = ptr + *cursor; - /* Lower 4 bits is data type/size */ - /* TODO: add more encodings if it becomes necessary */ - switch (encoding & 0xf) { - case DW_EH_PE_absptr: - if (!try_get_xbytes(memory, ptr, &data, 4, cursor)) { - return false; - } - *out_value = data; - return true; - case DW_EH_PE_udata4: - issigned = false; - case DW_EH_PE_sdata4: - if (!try_get_xbytes(memory, ptr, &data, 4, cursor)) { - return false; - } - break; - default: - ALOGE("unrecognized dwarf lower part encoding: 0x%x", encoding); - return false; - } - /* Higher 4 bits is modifier */ - /* TODO: add more encodings if it becomes necessary */ - switch (encoding & 0xf0) { - case 0: - *out_value = data; - break; - case DW_EH_PE_pcrel: - if (issigned) { - *out_value = addr + (int32_t)data; - } else { - *out_value = addr + data; - } - break; - /* Assuming ptr is correct base to calculate datarel */ - case DW_EH_PE_datarel: - if (issigned) { - *out_value = ptr + (int32_t)data; - } else { - *out_value = ptr + data; - } - break; - default: - ALOGE("unrecognized dwarf higher part encoding: 0x%x", encoding); - return false; - } - return true; -} - -/* Having PC find corresponding FDE by reading .eh_frame_hdr section data. */ -static uintptr_t find_fde(const memory_t* memory, - const map_info_t* map_info_list, uintptr_t pc) { - if (!pc) { - ALOGV("find_fde: pc is zero, no eh_frame"); - return 0; - } - const map_info_t* mi = find_map_info(map_info_list, pc); - if (!mi) { - ALOGV("find_fde: no map info for pc:0x%x", pc); - return 0; - } - const map_info_data_t* midata = mi->data; - if (!midata) { - ALOGV("find_fde: no eh_frame_hdr for map: start=0x%x, end=0x%x", mi->start, mi->end); - return 0; - } - - eh_frame_hdr_info_t eh_hdr_info; - memset(&eh_hdr_info, 0, sizeof(eh_frame_hdr_info_t)); - - /* Getting the first word of eh_frame_hdr: - 1st byte is version; - 2nd byte is encoding of pointer to eh_frames; - 3rd byte is encoding of count of FDEs in lookup table; - 4th byte is encoding of lookup table entries. - */ - uintptr_t eh_frame_hdr = midata->eh_frame_hdr; - uint32_t c = 0; - if (!try_get_byte(memory, eh_frame_hdr, &eh_hdr_info.version, &c)) return 0; - if (!try_get_byte(memory, eh_frame_hdr, &eh_hdr_info.eh_frame_ptr_enc, &c)) return 0; - if (!try_get_byte(memory, eh_frame_hdr, &eh_hdr_info.fde_count_enc, &c)) return 0; - if (!try_get_byte(memory, eh_frame_hdr, &eh_hdr_info.fde_table_enc, &c)) return 0; - - /* TODO: 3rd byte can be DW_EH_PE_omit, that means no lookup table available and we should - try to parse eh_frame instead. Not sure how often it may occur, skipping now. - */ - if (eh_hdr_info.version != 1) { - ALOGV("find_fde: eh_frame_hdr version %d is not supported", eh_hdr_info.version); - return 0; - } - /* Getting the data: - 2nd word is eh_frame pointer (normally not used, because lookup table has all we need); - 3rd word is count of FDEs in the lookup table; - starting from 4 word there is FDE lookup table (pairs of PC and FDE pointer) sorted by PC; - */ - if (!read_dwarf(memory, eh_frame_hdr, &eh_hdr_info.eh_frame_ptr, eh_hdr_info.eh_frame_ptr_enc, &c)) return 0; - if (!read_dwarf(memory, eh_frame_hdr, &eh_hdr_info.fde_count, eh_hdr_info.fde_count_enc, &c)) return 0; - ALOGV("find_fde: found %d FDEs", eh_hdr_info.fde_count); - - int32_t low = 0; - int32_t high = eh_hdr_info.fde_count; - uintptr_t start = 0; - uintptr_t fde = 0; - /* eh_frame_hdr + c points to lookup table at this point. */ - while (low <= high) { - uint32_t mid = (high + low)/2; - uint32_t entry = c + mid * 8; - if (!read_dwarf(memory, eh_frame_hdr, &start, eh_hdr_info.fde_table_enc, &entry)) return 0; - if (pc <= start) { - high = mid - 1; - } else { - low = mid + 1; - } - } - /* Value found is at high. */ - if (high < 0) { - ALOGV("find_fde: pc %x is out of FDE bounds: %x", pc, start); - return 0; - } - c += high * 8; - if (!read_dwarf(memory, eh_frame_hdr, &start, eh_hdr_info.fde_table_enc, &c)) return 0; - if (!read_dwarf(memory, eh_frame_hdr, &fde, eh_hdr_info.fde_table_enc, &c)) return 0; - ALOGV("pc 0x%x, ENTRY %d: start=0x%x, fde=0x%x", pc, high, start, fde); - return fde; -} - -/* Execute single dwarf instruction and update dwarf state accordingly. */ -static bool execute_dwarf(const memory_t* memory, uintptr_t ptr, cie_info_t* cie_info, - dwarf_state_t* dstate, uint32_t* cursor, - dwarf_state_t* stack, uint8_t* stack_ptr) { - uint8_t inst; - uint8_t op = 0; - - if (!try_get_byte(memory, ptr, &inst, cursor)) { - return false; - } - ALOGV("DW_CFA inst: 0x%x", inst); - - /* For some instructions upper 2 bits is opcode and lower 6 bits is operand. See dwarf-2.0 7.23. */ - if (inst & 0xc0) { - op = inst & 0x3f; - inst &= 0xc0; - } - - switch ((dwarf_CFA)inst) { - uint32_t reg = 0; - uint32_t offset = 0; - case DW_CFA_advance_loc: - dstate->loc += op * cie_info->code_align; - ALOGV("DW_CFA_advance_loc: %d to 0x%x", op, dstate->loc); - break; - case DW_CFA_offset: - if (!try_get_uleb128(memory, ptr, &offset, cursor)) return false; - dstate->regs[op].rule = 'o'; - dstate->regs[op].value = offset * cie_info->data_align; - ALOGV("DW_CFA_offset: r%d = o(%d)", op, dstate->regs[op].value); - break; - case DW_CFA_restore: - dstate->regs[op].rule = stack->regs[op].rule; - dstate->regs[op].value = stack->regs[op].value; - ALOGV("DW_CFA_restore: r%d = %c(%d)", op, dstate->regs[op].rule, dstate->regs[op].value); - break; - case DW_CFA_nop: - break; - case DW_CFA_set_loc: // probably we don't have it on mips. - if (!try_get_xbytes(memory, ptr, &offset, 4, cursor)) return false; - if (offset < dstate->loc) { - ALOGE("DW_CFA_set_loc: attempt to move location backward"); - return false; - } - dstate->loc = offset * cie_info->code_align; - ALOGV("DW_CFA_set_loc: %d to 0x%x", offset * cie_info->code_align, dstate->loc); - break; - case DW_CFA_advance_loc1: - if (!try_get_byte(memory, ptr, (uint8_t*)&offset, cursor)) return false; - dstate->loc += (uint8_t)offset * cie_info->code_align; - ALOGV("DW_CFA_advance_loc1: %d to 0x%x", (uint8_t)offset * cie_info->code_align, dstate->loc); - break; - case DW_CFA_advance_loc2: - if (!try_get_xbytes(memory, ptr, &offset, 2, cursor)) return false; - dstate->loc += (uint16_t)offset * cie_info->code_align; - ALOGV("DW_CFA_advance_loc2: %d to 0x%x", (uint16_t)offset * cie_info->code_align, dstate->loc); - break; - case DW_CFA_advance_loc4: - if (!try_get_xbytes(memory, ptr, &offset, 4, cursor)) return false; - dstate->loc += offset * cie_info->code_align; - ALOGV("DW_CFA_advance_loc4: %d to 0x%x", offset * cie_info->code_align, dstate->loc); - break; - case DW_CFA_offset_extended: // probably we don't have it on mips. - if (!try_get_uleb128(memory, ptr, ®, cursor)) return false; - if (!try_get_uleb128(memory, ptr, &offset, cursor)) return false; - if (reg >= DWARF_REGISTERS) { - ALOGE("DW_CFA_offset_extended: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS); - return false; - } - dstate->regs[reg].rule = 'o'; - dstate->regs[reg].value = offset * cie_info->data_align; - ALOGV("DW_CFA_offset_extended: r%d = o(%d)", reg, dstate->regs[reg].value); - break; - case DW_CFA_restore_extended: // probably we don't have it on mips. - if (!try_get_uleb128(memory, ptr, ®, cursor)) return false; - if (reg >= DWARF_REGISTERS) { - ALOGE("DW_CFA_restore_extended: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS); - return false; - } - dstate->regs[reg].rule = stack->regs[reg].rule; - dstate->regs[reg].value = stack->regs[reg].value; - ALOGV("DW_CFA_restore: r%d = %c(%d)", reg, dstate->regs[reg].rule, dstate->regs[reg].value); - break; - case DW_CFA_undefined: // probably we don't have it on mips. - if (!try_get_uleb128(memory, ptr, ®, cursor)) return false; - if (reg >= DWARF_REGISTERS) { - ALOGE("DW_CFA_undefined: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS); - return false; - } - dstate->regs[reg].rule = 'u'; - dstate->regs[reg].value = 0; - ALOGV("DW_CFA_undefined: r%d", reg); - break; - case DW_CFA_same_value: // probably we don't have it on mips. - if (!try_get_uleb128(memory, ptr, ®, cursor)) return false; - if (reg >= DWARF_REGISTERS) { - ALOGE("DW_CFA_undefined: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS); - return false; - } - dstate->regs[reg].rule = 's'; - dstate->regs[reg].value = 0; - ALOGV("DW_CFA_same_value: r%d", reg); - break; - case DW_CFA_register: // probably we don't have it on mips. - if (!try_get_uleb128(memory, ptr, ®, cursor)) return false; - /* that's new register actually, not offset */ - if (!try_get_uleb128(memory, ptr, &offset, cursor)) return false; - if (reg >= DWARF_REGISTERS || offset >= DWARF_REGISTERS) { - ALOGE("DW_CFA_register: r%d or r%d exceeds supported number of registers (%d)", reg, offset, DWARF_REGISTERS); - return false; - } - dstate->regs[reg].rule = 'r'; - dstate->regs[reg].value = offset; - ALOGV("DW_CFA_register: r%d = r(%d)", reg, dstate->regs[reg].value); - break; - case DW_CFA_remember_state: - if (*stack_ptr == DWARF_STATES_STACK) { - ALOGE("DW_CFA_remember_state: states stack overflow %d", *stack_ptr); - return false; - } - stack[(*stack_ptr)++] = *dstate; - ALOGV("DW_CFA_remember_state: stacktop moves to %d", *stack_ptr); - break; - case DW_CFA_restore_state: - /* We have CIE state saved at 0 position. It's not supposed to be taken - by DW_CFA_restore_state. */ - if (*stack_ptr == 1) { - ALOGE("DW_CFA_restore_state: states stack is empty"); - return false; - } - /* Don't touch location on restore. */ - uintptr_t saveloc = dstate->loc; - *dstate = stack[--*stack_ptr]; - dstate->loc = saveloc; - ALOGV("DW_CFA_restore_state: stacktop moves to %d", *stack_ptr); - break; - case DW_CFA_def_cfa: - if (!try_get_uleb128(memory, ptr, ®, cursor)) return false; - if (!try_get_uleb128(memory, ptr, &offset, cursor)) return false; - dstate->cfa_reg = reg; - dstate->cfa_off = offset; - ALOGV("DW_CFA_def_cfa: %x(r%d)", offset, reg); - break; - case DW_CFA_def_cfa_register: - if (!try_get_uleb128(memory, ptr, ®, cursor)) { - return false; - } - dstate->cfa_reg = reg; - ALOGV("DW_CFA_def_cfa_register: r%d", reg); - break; - case DW_CFA_def_cfa_offset: - if (!try_get_uleb128(memory, ptr, &offset, cursor)) { - return false; - } - dstate->cfa_off = offset; - ALOGV("DW_CFA_def_cfa_offset: %x", offset); - break; - default: - ALOGE("unrecognized DW_CFA_* instruction: 0x%x", inst); - return false; - } - return true; -} - -/* Restoring particular register value based on dwarf state. */ -static bool get_old_register_value(const memory_t* memory, uint32_t cfa, - dwarf_state_t* dstate, uint8_t reg, - unwind_state_t* state, unwind_state_t* newstate) { - uint32_t addr; - switch (dstate->regs[reg].rule) { - case 0: - /* We don't have dstate updated for this register, so assuming value kept the same. - Normally we should look into state and return current value as the old one - but we don't have all registers in state to handle this properly */ - ALOGV("get_old_register_value: value of r%d is the same", reg); - // for SP if it's not updated by dwarf rule we assume it's equal to CFA - // for PC if it's not updated by dwarf rule we assume it's equal to RA - if (reg == DWARF_SP) { - ALOGV("get_old_register_value: adjusting sp to CFA: 0x%x", cfa); - newstate->reg[reg] = cfa; - } else if (reg == DWARF_PC) { - ALOGV("get_old_register_value: adjusting PC to RA: 0x%x", newstate->reg[DWARF_RA]); - newstate->reg[reg] = newstate->reg[DWARF_RA]; - } else { - newstate->reg[reg] = state->reg[reg]; - } - break; - case 'o': - addr = cfa + (int32_t)dstate->regs[reg].value; - if (!try_get_word(memory, addr, &newstate->reg[reg])) { - ALOGE("get_old_register_value: can't read from 0x%x", addr); - return false; - } - ALOGV("get_old_register_value: r%d at 0x%x is 0x%x", reg, addr, newstate->reg[reg]); - break; - case 'r': - /* We don't have all registers in state so don't even try to look at 'r' */ - ALOGE("get_old_register_value: register lookup not implemented yet"); - break; - default: - ALOGE("get_old_register_value: unexpected rule:%c value:%d for register %d", - dstate->regs[reg].rule, (int32_t)dstate->regs[reg].value, reg); - return false; - } - return true; -} - -/* Updaing state based on dwarf state. */ -static bool update_state(const memory_t* memory, unwind_state_t* state, - dwarf_state_t* dstate) { - unwind_state_t newstate; - /* We can restore more registers here if we need them. Meanwile doing minimal work here. */ - /* Getting CFA. */ - uintptr_t cfa = 0; - if (dstate->cfa_reg == DWARF_SP) { - cfa = state->reg[DWARF_SP] + dstate->cfa_off; - } else if (dstate->cfa_reg == DWARF_FP) { - cfa = state->reg[DWARF_FP] + dstate->cfa_off; - } else { - ALOGE("update_state: unexpected CFA register: %d", dstate->cfa_reg); - return false; - } - ALOGV("update_state: new CFA: 0x%x", cfa); - - /* Update registers. Order is important to allow RA to propagate to PC */ - /* Getting FP. */ - if (!get_old_register_value(memory, cfa, dstate, DWARF_FP, state, &newstate)) return false; - /* Getting SP. */ - if (!get_old_register_value(memory, cfa, dstate, DWARF_SP, state, &newstate)) return false; - /* Getting RA. */ - if (!get_old_register_value(memory, cfa, dstate, DWARF_RA, state, &newstate)) return false; - /* Getting PC. */ - if (!get_old_register_value(memory, cfa, dstate, DWARF_PC, state, &newstate)) return false; - - ALOGV("update_state: PC: 0x%x; restore PC: 0x%x", state->reg[DWARF_PC], newstate.reg[DWARF_PC]); - ALOGV("update_state: RA: 0x%x; restore RA: 0x%x", state->reg[DWARF_RA], newstate.reg[DWARF_RA]); - ALOGV("update_state: FP: 0x%x; restore FP: 0x%x", state->reg[DWARF_FP], newstate.reg[DWARF_FP]); - ALOGV("update_state: SP: 0x%x; restore SP: 0x%x", state->reg[DWARF_SP], newstate.reg[DWARF_SP]); - - if (newstate.reg[DWARF_PC] == 0) - return false; - - /* End backtrace if registers do not change */ - if ((state->reg[DWARF_PC] == newstate.reg[DWARF_PC]) && - (state->reg[DWARF_RA] == newstate.reg[DWARF_RA]) && - (state->reg[DWARF_FP] == newstate.reg[DWARF_FP]) && - (state->reg[DWARF_SP] == newstate.reg[DWARF_SP])) - return false; - - *state = newstate; - return true; -} - -/* Execute CIE and FDE instructions for FDE found with find_fde. */ -static bool execute_fde(const memory_t* memory, - uintptr_t fde, - unwind_state_t* state) { - uint32_t fde_length = 0; - uint32_t cie_length = 0; - uintptr_t cie = 0; - uintptr_t cie_offset = 0; - cie_info_t cie_i; - cie_info_t* cie_info = &cie_i; - fde_info_t fde_i; - fde_info_t* fde_info = &fde_i; - dwarf_state_t dwarf_state; - dwarf_state_t* dstate = &dwarf_state; - dwarf_state_t stack[DWARF_STATES_STACK]; - uint8_t stack_ptr = 0; - - memset(dstate, 0, sizeof(dwarf_state_t)); - memset(cie_info, 0, sizeof(cie_info_t)); - memset(fde_info, 0, sizeof(fde_info_t)); - - /* Read common CIE or FDE area: - 1st word is length; - 2nd word is ID: 0 for CIE, CIE pointer for FDE. - */ - if (!try_get_word(memory, fde, &fde_length)) { - return false; - } - if ((int32_t)fde_length == -1) { - ALOGV("execute_fde: 64-bit dwarf detected, not implemented yet"); - return false; - } - if (!try_get_word(memory, fde + 4, &cie_offset)) { - return false; - } - if (cie_offset == 0) { - /* This is CIE. We shouldn't be here normally. */ - cie = fde; - cie_length = fde_length; - } else { - /* Find CIE. */ - /* Positive cie_offset goes backward from current field. */ - cie = fde + 4 - cie_offset; - if (!try_get_word(memory, cie, &cie_length)) { - return false; - } - if ((int32_t)cie_length == -1) { - ALOGV("execute_fde: 64-bit dwarf detected, not implemented yet"); - return false; - } - if (!try_get_word(memory, cie + 4, &cie_offset)) { - return false; - } - if (cie_offset != 0) { - ALOGV("execute_fde: can't find CIE"); - return false; - } - } - ALOGV("execute_fde: FDE length: %d", fde_length); - ALOGV("execute_fde: CIE pointer: %x", cie); - ALOGV("execute_fde: CIE length: %d", cie_length); - - /* Read CIE: - Augmentation independent: - 1st byte is version; - next x bytes is /0 terminated augmentation string; - next x bytes is unsigned LEB128 encoded code alignment factor; - next x bytes is signed LEB128 encoded data alignment factor; - next 1 (CIE version 1) or x (CIE version 3 unsigned LEB128) bytes is return register column; - Augmentation dependent: - if 'z' next x bytes is unsigned LEB128 encoded augmentation data size; - if 'L' next 1 byte is LSDA encoding; - if 'R' next 1 byte is FDE encoding; - if 'S' CIE represents signal handler stack frame; - if 'P' next 1 byte is personality encoding folowed by personality function pointer; - Next x bytes is CIE program. - */ - - uint32_t c = 8; - if (!try_get_byte(memory, cie, &cie_info->version, &c)) { - return false; - } - ALOGV("execute_fde: CIE version: %d", cie_info->version); - uint8_t ch; - do { - if (!try_get_byte(memory, cie, &ch, &c)) { - return false; - } - switch (ch) { - case '\0': break; - case 'z': cie_info->aug_z = 1; break; - case 'L': cie_info->aug_L = 1; break; - case 'R': cie_info->aug_R = 1; break; - case 'S': cie_info->aug_S = 1; break; - case 'P': cie_info->aug_P = 1; break; - default: - ALOGV("execute_fde: Unrecognized CIE augmentation char: '%c'", ch); - return false; - break; - } - } while (ch); - if (!try_get_uleb128(memory, cie, &cie_info->code_align, &c)) { - return false; - } - if (!try_get_sleb128(memory, cie, &cie_info->data_align, &c)) { - return false; - } - if (cie_info->version >= 3) { - if (!try_get_uleb128(memory, cie, &cie_info->reg, &c)) { - return false; - } - } else { - if (!try_get_byte(memory, cie, (uint8_t*)&cie_info->reg, &c)) { - return false; - } - } - ALOGV("execute_fde: CIE code alignment factor: %d", cie_info->code_align); - ALOGV("execute_fde: CIE data alignment factor: %d", cie_info->data_align); - if (cie_info->aug_z) { - if (!try_get_uleb128(memory, cie, &cie_info->aug_z, &c)) { - return false; - } - } - if (cie_info->aug_L) { - if (!try_get_byte(memory, cie, &cie_info->aug_L, &c)) { - return false; - } - } else { - /* Default encoding. */ - cie_info->aug_L = DW_EH_PE_absptr; - } - if (cie_info->aug_R) { - if (!try_get_byte(memory, cie, &cie_info->aug_R, &c)) { - return false; - } - } else { - /* Default encoding. */ - cie_info->aug_R = DW_EH_PE_absptr; - } - if (cie_info->aug_P) { - /* Get encoding of personality routine pointer. We don't use it now. */ - if (!try_get_byte(memory, cie, (uint8_t*)&cie_info->aug_P, &c)) { - return false; - } - /* Get routine pointer. */ - if (!read_dwarf(memory, cie, &cie_info->aug_P, (uint8_t)cie_info->aug_P, &c)) { - return false; - } - } - /* CIE program. */ - /* Length field itself (4 bytes) is not included into length. */ - stack[0] = *dstate; - stack_ptr = 1; - while (c < cie_length + 4) { - if (!execute_dwarf(memory, cie, cie_info, dstate, &c, stack, &stack_ptr)) { - return false; - } - } - - /* We went directly to CIE. Normally it shouldn't occur. */ - if (cie == fde) return true; - - /* Go back to FDE. */ - c = 8; - /* Read FDE: - Augmentation independent: - next x bytes (encoded as specified in CIE) is FDE starting address; - next x bytes (encoded as specified in CIE) is FDE number of instructions covered; - Augmentation dependent: - if 'z' next x bytes is unsigned LEB128 encoded augmentation data size; - if 'L' next x bytes is LSDA pointer (encoded as specified in CIE); - Next x bytes is FDE program. - */ - if (!read_dwarf(memory, fde, &fde_info->start, (uint8_t)cie_info->aug_R, &c)) { - return false; - } - dstate->loc = fde_info->start; - ALOGV("execute_fde: FDE start: %x", dstate->loc); - if (!read_dwarf(memory, fde, &fde_info->length, 0, &c)) { - return false; - } - ALOGV("execute_fde: FDE length: %x", fde_info->length); - if (cie_info->aug_z) { - if (!try_get_uleb128(memory, fde, &fde_info->aug_z, &c)) { - return false; - } - } - if (cie_info->aug_L && cie_info->aug_L != DW_EH_PE_omit) { - if (!read_dwarf(memory, fde, &fde_info->aug_L, cie_info->aug_L, &c)) { - return false; - } - } - /* FDE program. */ - /* Length field itself (4 bytes) is not included into length. */ - /* Save CIE state as 0 element of stack. Used by DW_CFA_restore. */ - stack[0] = *dstate; - stack_ptr = 1; - while (c < fde_length + 4 && state->reg[DWARF_PC] >= dstate->loc) { - if (!execute_dwarf(memory, fde, cie_info, dstate, &c, stack, &stack_ptr)) { - return false; - } - ALOGV("PC: %x, LOC: %x", state->reg[DWARF_PC], dstate->loc); - } - - return update_state(memory, state, dstate); -} - -static bool heuristic_state_update(const memory_t* memory, unwind_state_t* state) -{ - bool found_start = false; - int maxcheck = 1024; - int32_t stack_size = 0; - int32_t ra_offset = 0; - dwarf_state_t dwarf_state; - dwarf_state_t* dstate = &dwarf_state; - - static struct { - uint32_t insn; - uint32_t mask; - } frame0sig[] = { - {0x3c1c0000, 0xffff0000}, /* lui gp,xxxx */ - {0x279c0000, 0xffff0000}, /* addiu gp,gp,xxxx */ - {0x039fe021, 0xffffffff}, /* addu gp,gp,ra */ - }; - const int nframe0sig = sizeof(frame0sig)/sizeof(frame0sig[0]); - int f0 = nframe0sig; - memset(dstate, 0, sizeof(dwarf_state_t)); - - /* Search code backwards looking for function prologue */ - for (uint32_t pc = state->reg[DWARF_PC]-4; maxcheck-- > 0 && !found_start; pc -= 4) { - uint32_t op; - int32_t immediate; - - if (!try_get_word(memory, pc, &op)) - return false; - - // ALOGV("@0x%08x: 0x%08x\n", pc, op); - - // Check for frame 0 signature - if ((op & frame0sig[f0].mask) == frame0sig[f0].insn) { - if (f0 == 0) - return false; - f0--; - } - else { - f0 = nframe0sig; - } - - switch (op & 0xffff0000) { - case 0x27bd0000: // addiu sp, imm - // looking for stack being decremented - immediate = (((int32_t)op) << 16) >> 16; - if (immediate < 0) { - stack_size = -immediate; - ALOGV("@0x%08x: found stack adjustment=%d\n", pc, stack_size); - } - break; - case 0x039f0000: // e021 - - case 0xafbf0000: // sw ra, imm(sp) - ra_offset = (((int32_t)op) << 16) >> 16; - ALOGV("@0x%08x: found ra offset=%d\n", pc, ra_offset); - break; - case 0x3c1c0000: // lui gp - ALOGV("@0x%08x: found function boundary", pc); - found_start = true; - break; - default: - break; - } - } - - dstate->cfa_reg = DWARF_SP; - dstate->cfa_off = stack_size; - - if (ra_offset) { - dstate->regs[DWARF_RA].rule = 'o'; - dstate->regs[DWARF_RA].value = -stack_size + ra_offset; - } - - return update_state(memory, state, dstate); -} - -static ssize_t unwind_backtrace_common(const memory_t* memory, - const map_info_t* map_info_list, - unwind_state_t* state, backtrace_frame_t* backtrace, - size_t ignore_depth, size_t max_depth) { - - size_t ignored_frames = 0; - size_t returned_frames = 0; - - ALOGV("Unwinding tid: %d", memory->tid); - ALOGV("PC: %x", state->reg[DWARF_PC]); - ALOGV("RA: %x", state->reg[DWARF_RA]); - ALOGV("FP: %x", state->reg[DWARF_FP]); - ALOGV("SP: %x", state->reg[DWARF_SP]); - - for (size_t index = 0; returned_frames < max_depth; index++) { - uintptr_t fde = find_fde(memory, map_info_list, state->reg[DWARF_PC]); - backtrace_frame_t* frame = add_backtrace_entry( - index ? rewind_pc_arch(memory, state->reg[DWARF_PC]) : state->reg[DWARF_PC], - backtrace, ignore_depth, max_depth, - &ignored_frames, &returned_frames); - uint32_t stack_top = state->reg[DWARF_SP]; - - if (fde) { - /* Use FDE to update state */ - if (!execute_fde(memory, fde, state)) - break; - } - else { - /* FDE is not found, update state heuristically */ - if (!heuristic_state_update(memory, state)) - break; - } - - if (frame) { - frame->stack_top = stack_top; - if (stack_top < state->reg[DWARF_SP]) { - frame->stack_size = state->reg[DWARF_SP] - stack_top; - } - } - ALOGV("Stack: 0x%x ... 0x%x - %d bytes", frame->stack_top, state->reg[DWARF_SP], frame->stack_size); - } - return returned_frames; -} - -ssize_t unwind_backtrace_signal_arch(siginfo_t* siginfo __attribute__((unused)), void* sigcontext, - const map_info_t* map_info_list, - backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth) { - const ucontext_t* uc = (const ucontext_t*)sigcontext; - - unwind_state_t state; - state.reg[DWARF_PC] = uc->sc_pc; - state.reg[DWARF_RA] = uc->sc_regs[REG_RA]; - state.reg[DWARF_FP] = uc->sc_regs[REG_S8]; - state.reg[DWARF_SP] = uc->sc_regs[REG_SP]; - - ALOGV("unwind_backtrace_signal_arch: " - "ignore_depth=%d max_depth=%d pc=0x%08x sp=0x%08x ra=0x%08x\n", - ignore_depth, max_depth, state.reg[DWARF_PC], state.reg[DWARF_SP], state.reg[DWARF_RA]); - - memory_t memory; - init_memory(&memory, map_info_list); - return unwind_backtrace_common(&memory, map_info_list, - &state, backtrace, ignore_depth, max_depth); -} - -ssize_t unwind_backtrace_ptrace_arch(pid_t tid, const ptrace_context_t* context, - backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth) { - - user_regs_struct regs; - if (ptrace(PTRACE_GETREGS, tid, 0, ®s)) { - return -1; - } - - unwind_state_t state; - state.reg[DWARF_PC] = regs.epc; - state.reg[DWARF_RA] = regs.regs[REG_RA]; - state.reg[DWARF_FP] = regs.regs[REG_S8]; - state.reg[DWARF_SP] = regs.regs[REG_SP]; - - ALOGV("unwind_backtrace_ptrace_arch: " - "ignore_depth=%d max_depth=%d pc=0x%08x sp=0x%08x ra=0x%08x\n", - ignore_depth, max_depth, state.reg[DWARF_PC], state.reg[DWARF_SP], state.reg[DWARF_RA]); - - memory_t memory; - init_memory_ptrace(&memory, tid); - return unwind_backtrace_common(&memory, context->map_info_list, - &state, backtrace, ignore_depth, max_depth); -} diff --git a/libcorkscrew/arch-mips/dwarf.h b/libcorkscrew/arch-mips/dwarf.h deleted file mode 100644 index 8504ea0..0000000 --- a/libcorkscrew/arch-mips/dwarf.h +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright (C) 2013 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. - */ - -/* - * Dwarf2 data encoding flags. - */ - -#define DW_EH_PE_absptr 0x00 -#define DW_EH_PE_omit 0xff -#define DW_EH_PE_uleb128 0x01 -#define DW_EH_PE_udata2 0x02 -#define DW_EH_PE_udata4 0x03 -#define DW_EH_PE_udata8 0x04 -#define DW_EH_PE_sleb128 0x09 -#define DW_EH_PE_sdata2 0x0A -#define DW_EH_PE_sdata4 0x0B -#define DW_EH_PE_sdata8 0x0C -#define DW_EH_PE_signed 0x08 -#define DW_EH_PE_pcrel 0x10 -#define DW_EH_PE_textrel 0x20 -#define DW_EH_PE_datarel 0x30 -#define DW_EH_PE_funcrel 0x40 -#define DW_EH_PE_aligned 0x50 -#define DW_EH_PE_indirect 0x80 - -/* - * Dwarf2 call frame instructions. - */ - -typedef enum { - DW_CFA_advance_loc = 0x40, - DW_CFA_offset = 0x80, - DW_CFA_restore = 0xc0, - DW_CFA_nop = 0x00, - DW_CFA_set_loc = 0x01, - DW_CFA_advance_loc1 = 0x02, - DW_CFA_advance_loc2 = 0x03, - DW_CFA_advance_loc4 = 0x04, - DW_CFA_offset_extended = 0x05, - DW_CFA_restore_extended = 0x06, - DW_CFA_undefined = 0x07, - DW_CFA_same_value = 0x08, - DW_CFA_register = 0x09, - DW_CFA_remember_state = 0x0a, - DW_CFA_restore_state = 0x0b, - DW_CFA_def_cfa = 0x0c, - DW_CFA_def_cfa_register = 0x0d, - DW_CFA_def_cfa_offset = 0x0e -} dwarf_CFA; - -/* - * eh_frame_hdr information. -*/ - -typedef struct { - uint8_t version; - uint8_t eh_frame_ptr_enc; - uint8_t fde_count_enc; - uint8_t fde_table_enc; - uintptr_t eh_frame_ptr; - uint32_t fde_count; -} eh_frame_hdr_info_t; - -/* - * CIE information. -*/ - -typedef struct { - uint8_t version; - uint32_t code_align; - uint32_t data_align; - uint32_t reg; - uint32_t aug_z; - uint8_t aug_L; - uint8_t aug_R; - uint8_t aug_S; - uint32_t aug_P; -} cie_info_t; - -/* - * FDE information. -*/ - -typedef struct { - uint32_t start; - uint32_t length; // number of instructions covered by FDE - uint32_t aug_z; - uint32_t aug_L; -} fde_info_t; - -/* - * Dwarf state. -*/ - -/* Stack of states: required for DW_CFA_remember_state/DW_CFA_restore_state - 30 should be enough */ -#define DWARF_STATES_STACK 30 - -typedef struct { - char rule; // rule: o - offset(value); r - register(value) - uint32_t value; // value -} reg_rule_t; - -/* Dwarf preserved number of registers for mips */ -typedef enum - { - UNW_MIPS_R0, - UNW_MIPS_R1, - UNW_MIPS_R2, - UNW_MIPS_R3, - UNW_MIPS_R4, - UNW_MIPS_R5, - UNW_MIPS_R6, - UNW_MIPS_R7, - UNW_MIPS_R8, - UNW_MIPS_R9, - UNW_MIPS_R10, - UNW_MIPS_R11, - UNW_MIPS_R12, - UNW_MIPS_R13, - UNW_MIPS_R14, - UNW_MIPS_R15, - UNW_MIPS_R16, - UNW_MIPS_R17, - UNW_MIPS_R18, - UNW_MIPS_R19, - UNW_MIPS_R20, - UNW_MIPS_R21, - UNW_MIPS_R22, - UNW_MIPS_R23, - UNW_MIPS_R24, - UNW_MIPS_R25, - UNW_MIPS_R26, - UNW_MIPS_R27, - UNW_MIPS_R28, - UNW_MIPS_R29, - UNW_MIPS_R30, - UNW_MIPS_R31, - - UNW_MIPS_PC = 34, - - /* FIXME: Other registers! */ - - /* For MIPS, the CFA is the value of SP (r29) at the call site in the - previous frame. */ - UNW_MIPS_CFA, - - UNW_TDEP_LASTREG, - - UNW_TDEP_LAST_REG = UNW_MIPS_R31, - - UNW_TDEP_IP = UNW_MIPS_R31, - UNW_TDEP_SP = UNW_MIPS_R29, - UNW_TDEP_EH = UNW_MIPS_R0 /* FIXME. */ - - } -mips_regnum_t; - -#define DWARF_REGISTERS UNW_TDEP_LASTREG - -typedef struct { - uintptr_t loc; // location (ip) - uint8_t cfa_reg; // index of register where CFA location stored - intptr_t cfa_off; // offset - reg_rule_t regs[DWARF_REGISTERS]; // dwarf preserved registers for mips -} dwarf_state_t; - -/* DWARF registers we are caring about. */ - - -#define DWARF_SP UNW_MIPS_R29 -#define DWARF_RA UNW_MIPS_R31 -#define DWARF_PC UNW_MIPS_PC -#define DWARF_FP UNW_MIPS_CFA /* FIXME is this correct? */ diff --git a/libcorkscrew/arch-mips/ptrace-mips.c b/libcorkscrew/arch-mips/ptrace-mips.c deleted file mode 100644 index ba3b60a..0000000 --- a/libcorkscrew/arch-mips/ptrace-mips.c +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -#define LOG_TAG "Corkscrew" -//#define LOG_NDEBUG 0 - -#include "../ptrace-arch.h" - -#include <stddef.h> -#include <elf.h> -#include <cutils/log.h> - -static void load_eh_frame_hdr(pid_t pid, map_info_t* mi, uintptr_t *eh_frame_hdr) { - uint32_t elf_phoff; - uint32_t elf_phentsize_ehsize; - uint32_t elf_shentsize_phnum; - - - try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_phoff), &elf_phoff); - ALOGV("reading 0x%08x elf_phoff:%x", mi->start + offsetof(Elf32_Ehdr, e_phoff), elf_phoff); - try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_ehsize), &elf_phentsize_ehsize); - ALOGV("reading 0x%08x elf_phentsize_ehsize:%x", mi->start + offsetof(Elf32_Ehdr, e_ehsize), elf_phentsize_ehsize); - try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_phnum), &elf_shentsize_phnum); - ALOGV("reading 0x%08x elf_shentsize_phnum:%x", mi->start + offsetof(Elf32_Ehdr, e_phnum), elf_shentsize_phnum); - - - - if (try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_phoff), &elf_phoff) - && try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_ehsize), - &elf_phentsize_ehsize) - && try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_phnum), - &elf_shentsize_phnum)) { - uint32_t elf_phentsize = elf_phentsize_ehsize >> 16; - uint32_t elf_phnum = elf_shentsize_phnum & 0xffff; - for (uint32_t i = 0; i < elf_phnum; i++) { - uintptr_t elf_phdr = mi->start + elf_phoff + i * elf_phentsize; - uint32_t elf_phdr_type; - if (!try_get_word_ptrace(pid, elf_phdr + offsetof(Elf32_Phdr, p_type), &elf_phdr_type)) { - break; - } - if (elf_phdr_type == PT_GNU_EH_FRAME) { - uint32_t elf_phdr_offset; - if (!try_get_word_ptrace(pid, elf_phdr + offsetof(Elf32_Phdr, p_offset), - &elf_phdr_offset)) { - break; - } - *eh_frame_hdr = mi->start + elf_phdr_offset; - ALOGV("Parsed .eh_frame_hdr info for %s: start=0x%08x", mi->name, *eh_frame_hdr); - return; - } - } - } - *eh_frame_hdr = 0; -} - -void load_ptrace_map_info_data_arch(pid_t pid, map_info_t* mi, map_info_data_t* data) { - ALOGV("load_ptrace_map_info_data_arch"); - load_eh_frame_hdr(pid, mi, &data->eh_frame_hdr); -} - -void free_ptrace_map_info_data_arch(map_info_t* mi __attribute__((unused)), - map_info_data_t* data __attribute__((unused))) { - ALOGV("free_ptrace_map_info_data_arch"); -} diff --git a/libcorkscrew/arch-x86/backtrace-x86.c b/libcorkscrew/arch-x86/backtrace-x86.c deleted file mode 100755 index ef22821..0000000 --- a/libcorkscrew/arch-x86/backtrace-x86.c +++ /dev/null @@ -1,862 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -/* - * Backtracing functions for x86. - */ - -#define LOG_TAG "Corkscrew" -//#define LOG_NDEBUG 0 - -#include "../backtrace-arch.h" -#include "../backtrace-helper.h" -#include "../ptrace-arch.h" -#include <corkscrew/ptrace.h> -#include "dwarf.h" - -#include <stdlib.h> -#include <signal.h> -#include <stdbool.h> -#include <limits.h> -#include <errno.h> -#include <string.h> -#include <sys/ptrace.h> -#include <cutils/log.h> - -#if defined(__BIONIC__) - -#if defined(__BIONIC_HAVE_UCONTEXT_T) - -// Bionic offers the Linux kernel headers. -#include <asm/sigcontext.h> -#include <asm/ucontext.h> -typedef struct ucontext ucontext_t; - -#else /* __BIONIC_HAVE_UCONTEXT_T */ - -/* Old versions of the Android <signal.h> didn't define ucontext_t. */ - -typedef struct { - uint32_t gregs[32]; - void* fpregs; - uint32_t oldmask; - uint32_t cr2; -} mcontext_t; - -enum { - REG_GS = 0, REG_FS, REG_ES, REG_DS, - REG_EDI, REG_ESI, REG_EBP, REG_ESP, - REG_EBX, REG_EDX, REG_ECX, REG_EAX, - REG_TRAPNO, REG_ERR, REG_EIP, REG_CS, - REG_EFL, REG_UESP, REG_SS -}; - -/* Machine context at the time a signal was raised. */ -typedef struct ucontext { - uint32_t uc_flags; - struct ucontext* uc_link; - stack_t uc_stack; - mcontext_t uc_mcontext; - uint32_t uc_sigmask; -} ucontext_t; - -#endif /* __BIONIC_HAVE_UCONTEXT_T */ - -#elif defined(__APPLE__) - -#define _XOPEN_SOURCE -#include <ucontext.h> - -#else - -// glibc has its own renaming of the Linux kernel's structures. -#define __USE_GNU // For REG_EBP, REG_ESP, and REG_EIP. -#include <ucontext.h> - -#endif - -/* Unwind state. */ -typedef struct { - uint32_t reg[DWARF_REGISTERS]; -} unwind_state_t; - -typedef struct { - backtrace_frame_t* backtrace; - size_t ignore_depth; - size_t max_depth; - size_t ignored_frames; - size_t returned_frames; - memory_t memory; -} backtrace_state_t; - -uintptr_t rewind_pc_arch(const memory_t* memory __attribute__((unused)), uintptr_t pc) { - /* TODO: x86 instructions are 1-16 bytes, to define exact size of previous instruction - we have to disassemble from the function entry point up to pc. - Returning pc-1 is probably enough for now, the only drawback is that - it points somewhere between the first byte of instruction we are looking for and - the first byte of the next instruction. */ - - return pc-1; - /* TODO: We should adjust that for the signal frames and return pc for them instead of pc-1. - To recognize signal frames we should read cie_info property. */ -} - -/* Read byte through 4 byte cache. Usually we read byte by byte and updating cursor. */ -static bool try_get_byte(const memory_t* memory, uintptr_t ptr, uint8_t* out_value, uint32_t* cursor) { - static uintptr_t lastptr; - static uint32_t buf; - - ptr += *cursor; - - if (ptr < lastptr || lastptr + 3 < ptr) { - lastptr = (ptr >> 2) << 2; - if (!try_get_word(memory, lastptr, &buf)) { - return false; - } - } - *out_value = (uint8_t)((buf >> ((ptr & 3) * 8)) & 0xff); - ++*cursor; - return true; -} - -/* Getting X bytes. 4 is maximum for now. */ -static bool try_get_xbytes(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint8_t bytes, uint32_t* cursor) { - uint32_t data = 0; - if (bytes > 4) { - ALOGE("can't read more than 4 bytes, trying to read %d", bytes); - return false; - } - for (int i = 0; i < bytes; i++) { - uint8_t buf; - if (!try_get_byte(memory, ptr, &buf, cursor)) { - return false; - } - data |= (uint32_t)buf << (i * 8); - } - *out_value = data; - return true; -} - -/* Reads signed/unsigned LEB128 encoded data. From 1 to 4 bytes. */ -static bool try_get_leb128(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint32_t* cursor, bool sign_extend) { - uint8_t buf = 0; - uint32_t val = 0; - uint8_t c = 0; - do { - if (!try_get_byte(memory, ptr, &buf, cursor)) { - return false; - } - val |= ((uint32_t)buf & 0x7f) << (c * 7); - c++; - } while (buf & 0x80 && (c * 7) <= 32); - if (c * 7 > 32) { - ALOGE("%s: data exceeds expected 4 bytes maximum", __FUNCTION__); - return false; - } - if (sign_extend) { - if (buf & 0x40) { - val |= ((uint32_t)-1 << (c * 7)); - } - } - *out_value = val; - return true; -} - -/* Reads signed LEB128 encoded data. From 1 to 4 bytes. */ -static bool try_get_sleb128(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint32_t* cursor) { - return try_get_leb128(memory, ptr, out_value, cursor, true); -} - -/* Reads unsigned LEB128 encoded data. From 1 to 4 bytes. */ -static bool try_get_uleb128(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint32_t* cursor) { - return try_get_leb128(memory, ptr, out_value, cursor, false); -} - -/* Getting data encoded by dwarf encodings. */ -static bool read_dwarf(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint8_t encoding, uint32_t* cursor) { - uint32_t data = 0; - bool issigned = true; - uintptr_t addr = ptr + *cursor; - /* Lower 4 bits is data type/size */ - /* TODO: add more encodings if it becomes necessary */ - switch (encoding & 0xf) { - case DW_EH_PE_absptr: - if (!try_get_xbytes(memory, ptr, &data, 4, cursor)) { - return false; - } - *out_value = data; - return true; - case DW_EH_PE_udata4: - issigned = false; - case DW_EH_PE_sdata4: - if (!try_get_xbytes(memory, ptr, &data, 4, cursor)) { - return false; - } - break; - default: - ALOGE("unrecognized dwarf lower part encoding: 0x%x", encoding); - return false; - } - /* Higher 4 bits is modifier */ - /* TODO: add more encodings if it becomes necessary */ - switch (encoding & 0xf0) { - case 0: - *out_value = data; - break; - case DW_EH_PE_pcrel: - if (issigned) { - *out_value = addr + (int32_t)data; - } else { - *out_value = addr + data; - } - break; - /* Assuming ptr is correct base to calculate datarel */ - case DW_EH_PE_datarel: - if (issigned) { - *out_value = ptr + (int32_t)data; - } else { - *out_value = ptr + data; - } - break; - default: - ALOGE("unrecognized dwarf higher part encoding: 0x%x", encoding); - return false; - } - return true; -} - -/* Having PC find corresponding FDE by reading .eh_frame_hdr section data. */ -static uintptr_t find_fde(const memory_t* memory, - const map_info_t* map_info_list, uintptr_t pc) { - if (!pc) { - ALOGV("find_fde: pc is zero, no eh_frame"); - return 0; - } - const map_info_t* mi = find_map_info(map_info_list, pc); - if (!mi) { - ALOGV("find_fde: no map info for pc:0x%x", pc); - return 0; - } - const map_info_data_t* midata = mi->data; - if (!midata) { - ALOGV("find_fde: no eh_frame_hdr for map: start=0x%x, end=0x%x", mi->start, mi->end); - return 0; - } - - eh_frame_hdr_info_t eh_hdr_info; - memset(&eh_hdr_info, 0, sizeof(eh_frame_hdr_info_t)); - - /* Getting the first word of eh_frame_hdr: - 1st byte is version; - 2nd byte is encoding of pointer to eh_frames; - 3rd byte is encoding of count of FDEs in lookup table; - 4th byte is encoding of lookup table entries. - */ - uintptr_t eh_frame_hdr = midata->eh_frame_hdr; - uint32_t c = 0; - if (!try_get_byte(memory, eh_frame_hdr, &eh_hdr_info.version, &c)) return 0; - if (!try_get_byte(memory, eh_frame_hdr, &eh_hdr_info.eh_frame_ptr_enc, &c)) return 0; - if (!try_get_byte(memory, eh_frame_hdr, &eh_hdr_info.fde_count_enc, &c)) return 0; - if (!try_get_byte(memory, eh_frame_hdr, &eh_hdr_info.fde_table_enc, &c)) return 0; - - /* TODO: 3rd byte can be DW_EH_PE_omit, that means no lookup table available and we should - try to parse eh_frame instead. Not sure how often it may occur, skipping now. - */ - if (eh_hdr_info.version != 1) { - ALOGV("find_fde: eh_frame_hdr version %d is not supported", eh_hdr_info.version); - return 0; - } - /* Getting the data: - 2nd word is eh_frame pointer (normally not used, because lookup table has all we need); - 3rd word is count of FDEs in the lookup table; - starting from 4 word there is FDE lookup table (pairs of PC and FDE pointer) sorted by PC; - */ - if (!read_dwarf(memory, eh_frame_hdr, &eh_hdr_info.eh_frame_ptr, eh_hdr_info.eh_frame_ptr_enc, &c)) return 0; - if (!read_dwarf(memory, eh_frame_hdr, &eh_hdr_info.fde_count, eh_hdr_info.fde_count_enc, &c)) return 0; - ALOGV("find_fde: found %d FDEs", eh_hdr_info.fde_count); - - int32_t low = 0; - int32_t high = eh_hdr_info.fde_count; - uintptr_t start = 0; - uintptr_t fde = 0; - /* eh_frame_hdr + c points to lookup table at this point. */ - while (low <= high) { - uint32_t mid = (high + low)/2; - uint32_t entry = c + mid * 8; - if (!read_dwarf(memory, eh_frame_hdr, &start, eh_hdr_info.fde_table_enc, &entry)) return 0; - if (pc <= start) { - high = mid - 1; - } else { - low = mid + 1; - } - } - /* Value found is at high. */ - if (high < 0) { - ALOGV("find_fde: pc %x is out of FDE bounds: %x", pc, start); - return 0; - } - c += high * 8; - if (!read_dwarf(memory, eh_frame_hdr, &start, eh_hdr_info.fde_table_enc, &c)) return 0; - if (!read_dwarf(memory, eh_frame_hdr, &fde, eh_hdr_info.fde_table_enc, &c)) return 0; - ALOGV("pc 0x%x, ENTRY %d: start=0x%x, fde=0x%x", pc, high, start, fde); - return fde; -} - -/* Execute single dwarf instruction and update dwarf state accordingly. */ -static bool execute_dwarf(const memory_t* memory, uintptr_t ptr, cie_info_t* cie_info, - dwarf_state_t* dstate, uint32_t* cursor, - dwarf_state_t* stack, uint8_t* stack_ptr) { - uint8_t inst; - uint8_t op = 0; - - if (!try_get_byte(memory, ptr, &inst, cursor)) { - return false; - } - ALOGV("DW_CFA inst: 0x%x", inst); - - /* For some instructions upper 2 bits is opcode and lower 6 bits is operand. See dwarf-2.0 7.23. */ - if (inst & 0xc0) { - op = inst & 0x3f; - inst &= 0xc0; - } - - switch ((dwarf_CFA)inst) { - uint32_t reg = 0; - uint32_t offset = 0; - case DW_CFA_advance_loc: - dstate->loc += op * cie_info->code_align; - ALOGV("DW_CFA_advance_loc: %d to 0x%x", op, dstate->loc); - break; - case DW_CFA_offset: - if (!try_get_uleb128(memory, ptr, &offset, cursor)) return false; - dstate->regs[op].rule = 'o'; - dstate->regs[op].value = offset * cie_info->data_align; - ALOGV("DW_CFA_offset: r%d = o(%d)", op, dstate->regs[op].value); - break; - case DW_CFA_restore: - dstate->regs[op].rule = stack->regs[op].rule; - dstate->regs[op].value = stack->regs[op].value; - ALOGV("DW_CFA_restore: r%d = %c(%d)", op, dstate->regs[op].rule, dstate->regs[op].value); - break; - case DW_CFA_nop: - break; - case DW_CFA_set_loc: // probably we don't have it on x86. - if (!try_get_xbytes(memory, ptr, &offset, 4, cursor)) return false; - if (offset < dstate->loc) { - ALOGE("DW_CFA_set_loc: attempt to move location backward"); - return false; - } - dstate->loc = offset * cie_info->code_align; - ALOGV("DW_CFA_set_loc: %d to 0x%x", offset * cie_info->code_align, dstate->loc); - break; - case DW_CFA_advance_loc1: - if (!try_get_byte(memory, ptr, (uint8_t*)&offset, cursor)) return false; - dstate->loc += (uint8_t)offset * cie_info->code_align; - ALOGV("DW_CFA_advance_loc1: %d to 0x%x", (uint8_t)offset * cie_info->code_align, dstate->loc); - break; - case DW_CFA_advance_loc2: - if (!try_get_xbytes(memory, ptr, &offset, 2, cursor)) return false; - dstate->loc += (uint16_t)offset * cie_info->code_align; - ALOGV("DW_CFA_advance_loc2: %d to 0x%x", (uint16_t)offset * cie_info->code_align, dstate->loc); - break; - case DW_CFA_advance_loc4: - if (!try_get_xbytes(memory, ptr, &offset, 4, cursor)) return false; - dstate->loc += offset * cie_info->code_align; - ALOGV("DW_CFA_advance_loc4: %d to 0x%x", offset * cie_info->code_align, dstate->loc); - break; - case DW_CFA_offset_extended: // probably we don't have it on x86. - if (!try_get_uleb128(memory, ptr, ®, cursor)) return false; - if (!try_get_uleb128(memory, ptr, &offset, cursor)) return false; - if (reg >= DWARF_REGISTERS) { - ALOGE("DW_CFA_offset_extended: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS); - return false; - } - dstate->regs[reg].rule = 'o'; - dstate->regs[reg].value = offset * cie_info->data_align; - ALOGV("DW_CFA_offset_extended: r%d = o(%d)", reg, dstate->regs[reg].value); - break; - case DW_CFA_restore_extended: // probably we don't have it on x86. - if (!try_get_uleb128(memory, ptr, ®, cursor)) return false; - if (reg >= DWARF_REGISTERS) { - ALOGE("DW_CFA_restore_extended: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS); - return false; - } - dstate->regs[reg].rule = stack->regs[reg].rule; - dstate->regs[reg].value = stack->regs[reg].value; - ALOGV("DW_CFA_restore: r%d = %c(%d)", reg, dstate->regs[reg].rule, dstate->regs[reg].value); - break; - case DW_CFA_undefined: // probably we don't have it on x86. - if (!try_get_uleb128(memory, ptr, ®, cursor)) return false; - if (reg >= DWARF_REGISTERS) { - ALOGE("DW_CFA_undefined: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS); - return false; - } - dstate->regs[reg].rule = 'u'; - dstate->regs[reg].value = 0; - ALOGV("DW_CFA_undefined: r%d", reg); - break; - case DW_CFA_same_value: // probably we don't have it on x86. - if (!try_get_uleb128(memory, ptr, ®, cursor)) return false; - if (reg >= DWARF_REGISTERS) { - ALOGE("DW_CFA_undefined: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS); - return false; - } - dstate->regs[reg].rule = 's'; - dstate->regs[reg].value = 0; - ALOGV("DW_CFA_same_value: r%d", reg); - break; - case DW_CFA_register: // probably we don't have it on x86. - if (!try_get_uleb128(memory, ptr, ®, cursor)) return false; - /* that's new register actually, not offset */ - if (!try_get_uleb128(memory, ptr, &offset, cursor)) return false; - if (reg >= DWARF_REGISTERS || offset >= DWARF_REGISTERS) { - ALOGE("DW_CFA_register: r%d or r%d exceeds supported number of registers (%d)", reg, offset, DWARF_REGISTERS); - return false; - } - dstate->regs[reg].rule = 'r'; - dstate->regs[reg].value = offset; - ALOGV("DW_CFA_register: r%d = r(%d)", reg, dstate->regs[reg].value); - break; - case DW_CFA_remember_state: - if (*stack_ptr == DWARF_STATES_STACK) { - ALOGE("DW_CFA_remember_state: states stack overflow %d", *stack_ptr); - return false; - } - stack[(*stack_ptr)++] = *dstate; - ALOGV("DW_CFA_remember_state: stacktop moves to %d", *stack_ptr); - break; - case DW_CFA_restore_state: - /* We have CIE state saved at 0 position. It's not supposed to be taken - by DW_CFA_restore_state. */ - if (*stack_ptr == 1) { - ALOGE("DW_CFA_restore_state: states stack is empty"); - return false; - } - /* Don't touch location on restore. */ - uintptr_t saveloc = dstate->loc; - *dstate = stack[--*stack_ptr]; - dstate->loc = saveloc; - ALOGV("DW_CFA_restore_state: stacktop moves to %d", *stack_ptr); - break; - case DW_CFA_def_cfa: - if (!try_get_uleb128(memory, ptr, ®, cursor)) return false; - if (!try_get_uleb128(memory, ptr, &offset, cursor)) return false; - dstate->cfa_reg = reg; - dstate->cfa_off = offset; - ALOGV("DW_CFA_def_cfa: %x(r%d)", offset, reg); - break; - case DW_CFA_def_cfa_register: - if (!try_get_uleb128(memory, ptr, ®, cursor)) { - return false; - } - dstate->cfa_reg = reg; - ALOGV("DW_CFA_def_cfa_register: r%d", reg); - break; - case DW_CFA_def_cfa_offset: - if (!try_get_uleb128(memory, ptr, &offset, cursor)) { - return false; - } - dstate->cfa_off = offset; - ALOGV("DW_CFA_def_cfa_offset: %x", offset); - break; - default: - ALOGE("unrecognized DW_CFA_* instruction: 0x%x", inst); - return false; - } - return true; -} - -/* Restoring particular register value based on dwarf state. */ -static bool get_old_register_value(const memory_t* memory, uint32_t cfa, - dwarf_state_t* dstate, uint8_t reg, - unwind_state_t* state, unwind_state_t* newstate) { - uint32_t addr; - switch (dstate->regs[reg].rule) { - case 0: - /* We don't have dstate updated for this register, so assuming value kept the same. - Normally we should look into state and return current value as the old one - but we don't have all registers in state to handle this properly */ - ALOGV("get_old_register_value: value of r%d is the same", reg); - // for ESP if it's not updated by dwarf rule we assume it's equal to CFA - if (reg == DWARF_ESP) { - ALOGV("get_old_register_value: adjusting esp to CFA: 0x%x", cfa); - newstate->reg[reg] = cfa; - } else { - newstate->reg[reg] = state->reg[reg]; - } - break; - case 'o': - addr = cfa + (int32_t)dstate->regs[reg].value; - if (!try_get_word(memory, addr, &newstate->reg[reg])) { - ALOGE("get_old_register_value: can't read from 0x%x", addr); - return false; - } - ALOGV("get_old_register_value: r%d at 0x%x is 0x%x", reg, addr, newstate->reg[reg]); - break; - case 'r': - /* We don't have all registers in state so don't even try to look at 'r' */ - ALOGE("get_old_register_value: register lookup not implemented yet"); - break; - default: - ALOGE("get_old_register_value: unexpected rule:%c value:%d for register %d", - dstate->regs[reg].rule, (int32_t)dstate->regs[reg].value, reg); - return false; - } - return true; -} - -/* Updaing state based on dwarf state. */ -static bool update_state(const memory_t* memory, unwind_state_t* state, - dwarf_state_t* dstate) { - unwind_state_t newstate; - /* We can restore more registers here if we need them. Meanwile doing minimal work here. */ - /* Getting CFA. */ - uintptr_t cfa = 0; - if (dstate->cfa_reg == DWARF_ESP) { - cfa = state->reg[DWARF_ESP] + dstate->cfa_off; - } else if (dstate->cfa_reg == DWARF_EBP) { - cfa = state->reg[DWARF_EBP] + dstate->cfa_off; - } else { - ALOGE("update_state: unexpected CFA register: %d", dstate->cfa_reg); - return false; - } - ALOGV("update_state: new CFA: 0x%x", cfa); - /* Getting EIP. */ - if (!get_old_register_value(memory, cfa, dstate, DWARF_EIP, state, &newstate)) return false; - /* Getting EBP. */ - if (!get_old_register_value(memory, cfa, dstate, DWARF_EBP, state, &newstate)) return false; - /* Getting ESP. */ - if (!get_old_register_value(memory, cfa, dstate, DWARF_ESP, state, &newstate)) return false; - - ALOGV("update_state: IP: 0x%x; restore IP: 0x%x", state->reg[DWARF_EIP], newstate.reg[DWARF_EIP]); - ALOGV("update_state: EBP: 0x%x; restore EBP: 0x%x", state->reg[DWARF_EBP], newstate.reg[DWARF_EBP]); - ALOGV("update_state: ESP: 0x%x; restore ESP: 0x%x", state->reg[DWARF_ESP], newstate.reg[DWARF_ESP]); - *state = newstate; - return true; -} - -/* Execute CIE and FDE instructions for FDE found with find_fde. */ -static bool execute_fde(const memory_t* memory, - uintptr_t fde, - unwind_state_t* state) { - uint32_t fde_length = 0; - uint32_t cie_length = 0; - uintptr_t cie = 0; - uintptr_t cie_offset = 0; - cie_info_t cie_i; - cie_info_t* cie_info = &cie_i; - fde_info_t fde_i; - fde_info_t* fde_info = &fde_i; - dwarf_state_t dwarf_state; - dwarf_state_t* dstate = &dwarf_state; - dwarf_state_t stack[DWARF_STATES_STACK]; - uint8_t stack_ptr = 0; - - memset(dstate, 0, sizeof(dwarf_state_t)); - memset(cie_info, 0, sizeof(cie_info_t)); - memset(fde_info, 0, sizeof(fde_info_t)); - - /* Read common CIE or FDE area: - 1st word is length; - 2nd word is ID: 0 for CIE, CIE pointer for FDE. - */ - if (!try_get_word(memory, fde, &fde_length)) { - return false; - } - if ((int32_t)fde_length == -1) { - ALOGV("execute_fde: 64-bit dwarf detected, not implemented yet"); - return false; - } - if (!try_get_word(memory, fde + 4, &cie_offset)) { - return false; - } - if (cie_offset == 0) { - /* This is CIE. We shouldn't be here normally. */ - cie = fde; - cie_length = fde_length; - } else { - /* Find CIE. */ - /* Positive cie_offset goes backward from current field. */ - cie = fde + 4 - cie_offset; - if (!try_get_word(memory, cie, &cie_length)) { - return false; - } - if ((int32_t)cie_length == -1) { - ALOGV("execute_fde: 64-bit dwarf detected, not implemented yet"); - return false; - } - if (!try_get_word(memory, cie + 4, &cie_offset)) { - return false; - } - if (cie_offset != 0) { - ALOGV("execute_fde: can't find CIE"); - return false; - } - } - ALOGV("execute_fde: FDE length: %d", fde_length); - ALOGV("execute_fde: CIE pointer: %x", cie); - ALOGV("execute_fde: CIE length: %d", cie_length); - - /* Read CIE: - Augmentation independent: - 1st byte is version; - next x bytes is /0 terminated augmentation string; - next x bytes is unsigned LEB128 encoded code alignment factor; - next x bytes is signed LEB128 encoded data alignment factor; - next 1 (CIE version 1) or x (CIE version 3 unsigned LEB128) bytes is return register column; - Augmentation dependent: - if 'z' next x bytes is unsigned LEB128 encoded augmentation data size; - if 'L' next 1 byte is LSDA encoding; - if 'R' next 1 byte is FDE encoding; - if 'S' CIE represents signal handler stack frame; - if 'P' next 1 byte is personality encoding folowed by personality function pointer; - Next x bytes is CIE program. - */ - - uint32_t c = 8; - if (!try_get_byte(memory, cie, &cie_info->version, &c)) { - return false; - } - ALOGV("execute_fde: CIE version: %d", cie_info->version); - uint8_t ch; - do { - if (!try_get_byte(memory, cie, &ch, &c)) { - return false; - } - switch (ch) { - case '\0': break; - case 'z': cie_info->aug_z = 1; break; - case 'L': cie_info->aug_L = 1; break; - case 'R': cie_info->aug_R = 1; break; - case 'S': cie_info->aug_S = 1; break; - case 'P': cie_info->aug_P = 1; break; - default: - ALOGV("execute_fde: Unrecognized CIE augmentation char: '%c'", ch); - return false; - break; - } - } while (ch); - if (!try_get_uleb128(memory, cie, &cie_info->code_align, &c)) { - return false; - } - if (!try_get_sleb128(memory, cie, &cie_info->data_align, &c)) { - return false; - } - if (cie_info->version >= 3) { - if (!try_get_uleb128(memory, cie, &cie_info->reg, &c)) { - return false; - } - } else { - if (!try_get_byte(memory, cie, (uint8_t*)&cie_info->reg, &c)) { - return false; - } - } - ALOGV("execute_fde: CIE code alignment factor: %d", cie_info->code_align); - ALOGV("execute_fde: CIE data alignment factor: %d", cie_info->data_align); - if (cie_info->aug_z) { - if (!try_get_uleb128(memory, cie, &cie_info->aug_z, &c)) { - return false; - } - } - if (cie_info->aug_L) { - if (!try_get_byte(memory, cie, &cie_info->aug_L, &c)) { - return false; - } - } else { - /* Default encoding. */ - cie_info->aug_L = DW_EH_PE_absptr; - } - if (cie_info->aug_R) { - if (!try_get_byte(memory, cie, &cie_info->aug_R, &c)) { - return false; - } - } else { - /* Default encoding. */ - cie_info->aug_R = DW_EH_PE_absptr; - } - if (cie_info->aug_P) { - /* Get encoding of personality routine pointer. We don't use it now. */ - if (!try_get_byte(memory, cie, (uint8_t*)&cie_info->aug_P, &c)) { - return false; - } - /* Get routine pointer. */ - if (!read_dwarf(memory, cie, &cie_info->aug_P, (uint8_t)cie_info->aug_P, &c)) { - return false; - } - } - /* CIE program. */ - /* Length field itself (4 bytes) is not included into length. */ - stack[0] = *dstate; - stack_ptr = 1; - while (c < cie_length + 4) { - if (!execute_dwarf(memory, cie, cie_info, dstate, &c, stack, &stack_ptr)) { - return false; - } - } - - /* We went directly to CIE. Normally it shouldn't occur. */ - if (cie == fde) return true; - - /* Go back to FDE. */ - c = 8; - /* Read FDE: - Augmentation independent: - next x bytes (encoded as specified in CIE) is FDE starting address; - next x bytes (encoded as specified in CIE) is FDE number of instructions covered; - Augmentation dependent: - if 'z' next x bytes is unsigned LEB128 encoded augmentation data size; - if 'L' next x bytes is LSDA pointer (encoded as specified in CIE); - Next x bytes is FDE program. - */ - if (!read_dwarf(memory, fde, &fde_info->start, (uint8_t)cie_info->aug_R, &c)) { - return false; - } - dstate->loc = fde_info->start; - ALOGV("execute_fde: FDE start: %x", dstate->loc); - if (!read_dwarf(memory, fde, &fde_info->length, 0, &c)) { - return false; - } - ALOGV("execute_fde: FDE length: %x", fde_info->length); - if (cie_info->aug_z) { - if (!try_get_uleb128(memory, fde, &fde_info->aug_z, &c)) { - return false; - } - } - if (cie_info->aug_L && cie_info->aug_L != DW_EH_PE_omit) { - if (!read_dwarf(memory, fde, &fde_info->aug_L, cie_info->aug_L, &c)) { - return false; - } - } - /* FDE program. */ - /* Length field itself (4 bytes) is not included into length. */ - /* Save CIE state as 0 element of stack. Used by DW_CFA_restore. */ - stack[0] = *dstate; - stack_ptr = 1; - while (c < fde_length + 4 && state->reg[DWARF_EIP] >= dstate->loc) { - if (!execute_dwarf(memory, fde, cie_info, dstate, &c, stack, &stack_ptr)) { - return false; - } - ALOGV("IP: %x, LOC: %x", state->reg[DWARF_EIP], dstate->loc); - } - - return update_state(memory, state, dstate); -} - -static ssize_t unwind_backtrace_common(const memory_t* memory, - const map_info_t* map_info_list, - unwind_state_t* state, backtrace_frame_t* backtrace, - size_t ignore_depth, size_t max_depth) { - - size_t ignored_frames = 0; - size_t returned_frames = 0; - - ALOGV("Unwinding tid: %d", memory->tid); - ALOGV("IP: %x", state->reg[DWARF_EIP]); - ALOGV("BP: %x", state->reg[DWARF_EBP]); - ALOGV("SP: %x", state->reg[DWARF_ESP]); - - for (size_t index = 0; returned_frames < max_depth; index++) { - uintptr_t fde = find_fde(memory, map_info_list, state->reg[DWARF_EIP]); - /* FDE is not found, it may happen if stack is corrupted or calling wrong adress. - Getting return address from stack. - */ - if (!fde) { - uint32_t ip; - ALOGV("trying to restore registers from stack"); - if (!try_get_word(memory, state->reg[DWARF_EBP] + 4, &ip) || - ip == state->reg[DWARF_EIP]) { - ALOGV("can't get IP from stack"); - break; - } - /* We've been able to get IP from stack so recording the frame before continue. */ - backtrace_frame_t* frame = add_backtrace_entry( - index ? rewind_pc_arch(memory, state->reg[DWARF_EIP]) : state->reg[DWARF_EIP], - backtrace, ignore_depth, max_depth, - &ignored_frames, &returned_frames); - state->reg[DWARF_EIP] = ip; - state->reg[DWARF_ESP] = state->reg[DWARF_EBP] + 8; - if (!try_get_word(memory, state->reg[DWARF_EBP], &state->reg[DWARF_EBP])) { - ALOGV("can't get EBP from stack"); - break; - } - ALOGV("restore IP: %x", state->reg[DWARF_EIP]); - ALOGV("restore BP: %x", state->reg[DWARF_EBP]); - ALOGV("restore SP: %x", state->reg[DWARF_ESP]); - continue; - } - backtrace_frame_t* frame = add_backtrace_entry( - index ? rewind_pc_arch(memory, state->reg[DWARF_EIP]) : state->reg[DWARF_EIP], - backtrace, ignore_depth, max_depth, - &ignored_frames, &returned_frames); - - uint32_t stack_top = state->reg[DWARF_ESP]; - - if (!execute_fde(memory, fde, state)) break; - - if (frame) { - frame->stack_top = stack_top; - if (stack_top < state->reg[DWARF_ESP]) { - frame->stack_size = state->reg[DWARF_ESP] - stack_top; - } - } - ALOGV("Stack: 0x%x ... 0x%x - %d bytes", frame->stack_top, state->reg[DWARF_ESP], frame->stack_size); - } - return returned_frames; -} - -ssize_t unwind_backtrace_signal_arch(siginfo_t* siginfo __attribute__((unused)), void* sigcontext, - const map_info_t* map_info_list, - backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth) { - const ucontext_t* uc = (const ucontext_t*)sigcontext; - - unwind_state_t state; -#if defined(__APPLE__) - state.reg[DWARF_EBP] = uc->uc_mcontext->__ss.__ebp; - state.reg[DWARF_ESP] = uc->uc_mcontext->__ss.__esp; - state.reg[DWARF_EIP] = uc->uc_mcontext->__ss.__eip; -#else - state.reg[DWARF_EBP] = uc->uc_mcontext.gregs[REG_EBP]; - state.reg[DWARF_ESP] = uc->uc_mcontext.gregs[REG_ESP]; - state.reg[DWARF_EIP] = uc->uc_mcontext.gregs[REG_EIP]; -#endif - - memory_t memory; - init_memory(&memory, map_info_list); - return unwind_backtrace_common(&memory, map_info_list, - &state, backtrace, ignore_depth, max_depth); -} - -ssize_t unwind_backtrace_ptrace_arch(pid_t tid, const ptrace_context_t* context, - backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth) { -#if defined(__APPLE__) - return -1; -#else - pt_regs_x86_t regs; - if (ptrace(PTRACE_GETREGS, tid, 0, ®s)) { - return -1; - } - - unwind_state_t state; - state.reg[DWARF_EBP] = regs.ebp; - state.reg[DWARF_EIP] = regs.eip; - state.reg[DWARF_ESP] = regs.esp; - - memory_t memory; - init_memory_ptrace(&memory, tid); - return unwind_backtrace_common(&memory, context->map_info_list, - &state, backtrace, ignore_depth, max_depth); -#endif -} diff --git a/libcorkscrew/arch-x86/dwarf.h b/libcorkscrew/arch-x86/dwarf.h deleted file mode 100755 index 962fc55..0000000 --- a/libcorkscrew/arch-x86/dwarf.h +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (C) 2013 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. - */ - -/* - * Dwarf2 data encoding flags. - */ - -#define DW_EH_PE_absptr 0x00 -#define DW_EH_PE_omit 0xff -#define DW_EH_PE_uleb128 0x01 -#define DW_EH_PE_udata2 0x02 -#define DW_EH_PE_udata4 0x03 -#define DW_EH_PE_udata8 0x04 -#define DW_EH_PE_sleb128 0x09 -#define DW_EH_PE_sdata2 0x0A -#define DW_EH_PE_sdata4 0x0B -#define DW_EH_PE_sdata8 0x0C -#define DW_EH_PE_signed 0x08 -#define DW_EH_PE_pcrel 0x10 -#define DW_EH_PE_textrel 0x20 -#define DW_EH_PE_datarel 0x30 -#define DW_EH_PE_funcrel 0x40 -#define DW_EH_PE_aligned 0x50 -#define DW_EH_PE_indirect 0x80 - -/* - * Dwarf2 call frame instructions. - */ - -typedef enum { - DW_CFA_advance_loc = 0x40, - DW_CFA_offset = 0x80, - DW_CFA_restore = 0xc0, - DW_CFA_nop = 0x00, - DW_CFA_set_loc = 0x01, - DW_CFA_advance_loc1 = 0x02, - DW_CFA_advance_loc2 = 0x03, - DW_CFA_advance_loc4 = 0x04, - DW_CFA_offset_extended = 0x05, - DW_CFA_restore_extended = 0x06, - DW_CFA_undefined = 0x07, - DW_CFA_same_value = 0x08, - DW_CFA_register = 0x09, - DW_CFA_remember_state = 0x0a, - DW_CFA_restore_state = 0x0b, - DW_CFA_def_cfa = 0x0c, - DW_CFA_def_cfa_register = 0x0d, - DW_CFA_def_cfa_offset = 0x0e -} dwarf_CFA; - -/* - * eh_frame_hdr information. -*/ - -typedef struct { - uint8_t version; - uint8_t eh_frame_ptr_enc; - uint8_t fde_count_enc; - uint8_t fde_table_enc; - uintptr_t eh_frame_ptr; - uint32_t fde_count; -} eh_frame_hdr_info_t; - -/* - * CIE information. -*/ - -typedef struct { - uint8_t version; - uint32_t code_align; - uint32_t data_align; - uint32_t reg; - uint32_t aug_z; - uint8_t aug_L; - uint8_t aug_R; - uint8_t aug_S; - uint32_t aug_P; -} cie_info_t; - -/* - * FDE information. -*/ - -typedef struct { - uint32_t start; - uint32_t length; // number of instructions covered by FDE - uint32_t aug_z; - uint32_t aug_L; -} fde_info_t; - -/* - * Dwarf state. -*/ - -/* Stack of states: required for DW_CFA_remember_state/DW_CFA_restore_state - 30 should be enough */ -#define DWARF_STATES_STACK 30 - -typedef struct { - char rule; // rule: o - offset(value); r - register(value) - uint32_t value; // value -} reg_rule_t; - -/* Dwarf preserved number of registers for x86. */ - -#define DWARF_REGISTERS 17 - -typedef struct { - uintptr_t loc; // location (ip) - uint8_t cfa_reg; // index of register where CFA location stored - intptr_t cfa_off; // offset - reg_rule_t regs[DWARF_REGISTERS]; // dwarf preserved registers for x86 -} dwarf_state_t; - -/* DWARF registers we are caring about. */ - -#define DWARF_EAX 0 -#define DWARF_ECX 1 -#define DWARF_EDX 2 -#define DWARF_EBX 3 -#define DWARF_ESP 4 -#define DWARF_EBP 5 -#define DWARF_ESI 6 -#define DWARF_EDI 7 -#define DWARF_EIP 8 - - diff --git a/libcorkscrew/arch-x86/ptrace-x86.c b/libcorkscrew/arch-x86/ptrace-x86.c deleted file mode 100755 index 9c49b93..0000000 --- a/libcorkscrew/arch-x86/ptrace-x86.c +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -#define LOG_TAG "Corkscrew" -//#define LOG_NDEBUG 0 - -#include "../ptrace-arch.h" - -#include <stddef.h> -#include <elf.h> -#include <cutils/log.h> - -static void load_eh_frame_hdr(pid_t pid, map_info_t* mi, uintptr_t *eh_frame_hdr) { - uint32_t elf_phoff; - uint32_t elf_phentsize_ehsize; - uint32_t elf_shentsize_phnum; - if (try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_phoff), &elf_phoff) - && try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_ehsize), - &elf_phentsize_ehsize) - && try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_phnum), - &elf_shentsize_phnum)) { - uint32_t elf_phentsize = elf_phentsize_ehsize >> 16; - uint32_t elf_phnum = elf_shentsize_phnum & 0xffff; - for (uint32_t i = 0; i < elf_phnum; i++) { - uintptr_t elf_phdr = mi->start + elf_phoff + i * elf_phentsize; - uint32_t elf_phdr_type; - if (!try_get_word_ptrace(pid, elf_phdr + offsetof(Elf32_Phdr, p_type), &elf_phdr_type)) { - break; - } - if (elf_phdr_type == PT_GNU_EH_FRAME) { - uint32_t elf_phdr_offset; - if (!try_get_word_ptrace(pid, elf_phdr + offsetof(Elf32_Phdr, p_offset), - &elf_phdr_offset)) { - break; - } - *eh_frame_hdr = mi->start + elf_phdr_offset; - ALOGV("Parsed .eh_frame_hdr info for %s: start=0x%08x", mi->name, *eh_frame_hdr); - return; - } - } - } - *eh_frame_hdr = 0; -} - -void load_ptrace_map_info_data_arch(pid_t pid, map_info_t* mi, map_info_data_t* data) { - load_eh_frame_hdr(pid, mi, &data->eh_frame_hdr); -} - -void free_ptrace_map_info_data_arch(map_info_t* mi __attribute__((unused)), - map_info_data_t* data __attribute__((unused))) { -} diff --git a/libcorkscrew/backtrace-arch.h b/libcorkscrew/backtrace-arch.h deleted file mode 100644 index a46f80b..0000000 --- a/libcorkscrew/backtrace-arch.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -/* Architecture dependent functions. */ - -#ifndef _CORKSCREW_BACKTRACE_ARCH_H -#define _CORKSCREW_BACKTRACE_ARCH_H - -#include "ptrace-arch.h" -#include <corkscrew/backtrace.h> - -#include <signal.h> - -#ifdef __cplusplus -extern "C" { -#endif - -/* Rewind the program counter by one instruction. */ -uintptr_t rewind_pc_arch(const memory_t* memory, uintptr_t pc); - -ssize_t unwind_backtrace_signal_arch(siginfo_t* siginfo, void* sigcontext, - const map_info_t* map_info_list, - backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth); - -ssize_t unwind_backtrace_ptrace_arch(pid_t tid, const ptrace_context_t* context, - backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth); - -#ifdef __cplusplus -} -#endif - -#endif // _CORKSCREW_BACKTRACE_ARCH_H diff --git a/libcorkscrew/backtrace-helper.c b/libcorkscrew/backtrace-helper.c deleted file mode 100644 index bf9d3f3..0000000 --- a/libcorkscrew/backtrace-helper.c +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -#define LOG_TAG "Corkscrew" -//#define LOG_NDEBUG 0 - -#include "backtrace-helper.h" - -#include <cutils/log.h> - -backtrace_frame_t* add_backtrace_entry(uintptr_t pc, backtrace_frame_t* backtrace, - size_t ignore_depth, size_t max_depth, - size_t* ignored_frames, size_t* returned_frames) { - if (*ignored_frames < ignore_depth) { - *ignored_frames += 1; - return NULL; - } - if (*returned_frames >= max_depth) { - return NULL; - } - backtrace_frame_t* frame = &backtrace[*returned_frames]; - frame->absolute_pc = pc; - frame->stack_top = 0; - frame->stack_size = 0; - *returned_frames += 1; - return frame; -} diff --git a/libcorkscrew/backtrace-helper.h b/libcorkscrew/backtrace-helper.h deleted file mode 100644 index 4d8a874..0000000 --- a/libcorkscrew/backtrace-helper.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -/* Backtrace helper functions. */ - -#ifndef _CORKSCREW_BACKTRACE_HELPER_H -#define _CORKSCREW_BACKTRACE_HELPER_H - -#include <corkscrew/backtrace.h> -#include <sys/types.h> -#include <stdbool.h> - -#ifdef __cplusplus -extern "C" { -#endif - -/* - * Add a program counter to a backtrace if it will fit. - * Returns the newly added frame, or NULL if none. - */ -backtrace_frame_t* add_backtrace_entry(uintptr_t pc, - backtrace_frame_t* backtrace, - size_t ignore_depth, size_t max_depth, - size_t* ignored_frames, size_t* returned_frames); - -#ifdef __cplusplus -} -#endif - -#endif // _CORKSCREW_BACKTRACE_HELPER_H diff --git a/libcorkscrew/backtrace.c b/libcorkscrew/backtrace.c deleted file mode 100644 index f1dd61d..0000000 --- a/libcorkscrew/backtrace.c +++ /dev/null @@ -1,335 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -#define LOG_TAG "Corkscrew" -//#define LOG_NDEBUG 0 - -#include "backtrace-arch.h" -#include "backtrace-helper.h" -#include "ptrace-arch.h" -#include <corkscrew/map_info.h> -#include <corkscrew/symbol_table.h> -#include <corkscrew/ptrace.h> -#include <corkscrew/demangle.h> - -#include <unistd.h> -#include <signal.h> -#include <stdlib.h> -#include <string.h> -#include <pthread.h> -#include <unwind.h> -#include <cutils/log.h> -#include <cutils/atomic.h> - -#define __USE_GNU // For dladdr(3) in glibc. -#include <dlfcn.h> - -#if defined(__BIONIC__) - -// Bionic implements and exports gettid but only implements tgkill. -extern int tgkill(int tgid, int tid, int sig); - -#elif defined(__APPLE__) - -#include <sys/syscall.h> - -// Mac OS >= 10.6 has a system call equivalent to Linux's gettid(). -static pid_t gettid() { - return syscall(SYS_thread_selfid); -} - -#else - -// glibc doesn't implement or export either gettid or tgkill. - -#include <unistd.h> -#include <sys/syscall.h> - -static pid_t gettid() { - return syscall(__NR_gettid); -} - -static int tgkill(int tgid, int tid, int sig) { - return syscall(__NR_tgkill, tgid, tid, sig); -} - -#endif - -typedef struct { - backtrace_frame_t* backtrace; - size_t ignore_depth; - size_t max_depth; - size_t ignored_frames; - size_t returned_frames; - memory_t memory; -} backtrace_state_t; - -static _Unwind_Reason_Code unwind_backtrace_callback(struct _Unwind_Context* context, void* arg) { - backtrace_state_t* state = (backtrace_state_t*)arg; - uintptr_t pc = _Unwind_GetIP(context); - if (pc) { - // TODO: Get information about the stack layout from the _Unwind_Context. - // This will require a new architecture-specific function to query - // the appropriate registers. Current callers of unwind_backtrace - // don't need this information, so we won't bother collecting it just yet. - add_backtrace_entry(rewind_pc_arch(&state->memory, pc), state->backtrace, - state->ignore_depth, state->max_depth, - &state->ignored_frames, &state->returned_frames); - } - return state->returned_frames < state->max_depth ? _URC_NO_REASON : _URC_END_OF_STACK; -} - -ssize_t unwind_backtrace(backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth) { - ALOGV("Unwinding current thread %d.", gettid()); - - map_info_t* milist = acquire_my_map_info_list(); - - backtrace_state_t state; - state.backtrace = backtrace; - state.ignore_depth = ignore_depth; - state.max_depth = max_depth; - state.ignored_frames = 0; - state.returned_frames = 0; - init_memory(&state.memory, milist); - - _Unwind_Reason_Code rc = _Unwind_Backtrace(unwind_backtrace_callback, &state); - - release_my_map_info_list(milist); - - if (state.returned_frames) { - return state.returned_frames; - } - return rc == _URC_END_OF_STACK ? 0 : -1; -} - -#ifdef CORKSCREW_HAVE_ARCH -static const int32_t STATE_DUMPING = -1; -static const int32_t STATE_DONE = -2; -static const int32_t STATE_CANCEL = -3; - -static pthread_mutex_t g_unwind_signal_mutex = PTHREAD_MUTEX_INITIALIZER; -static volatile struct { - int32_t tid_state; - const map_info_t* map_info_list; - backtrace_frame_t* backtrace; - size_t ignore_depth; - size_t max_depth; - size_t returned_frames; -} g_unwind_signal_state; - -static void unwind_backtrace_thread_signal_handler(int n __attribute__((unused)), siginfo_t* siginfo, void* sigcontext) { - if (!android_atomic_acquire_cas(gettid(), STATE_DUMPING, &g_unwind_signal_state.tid_state)) { - g_unwind_signal_state.returned_frames = unwind_backtrace_signal_arch( - siginfo, sigcontext, - g_unwind_signal_state.map_info_list, - g_unwind_signal_state.backtrace, - g_unwind_signal_state.ignore_depth, - g_unwind_signal_state.max_depth); - android_atomic_release_store(STATE_DONE, &g_unwind_signal_state.tid_state); - } else { - ALOGV("Received spurious SIGURG on thread %d that was intended for thread %d.", - gettid(), android_atomic_acquire_load(&g_unwind_signal_state.tid_state)); - } -} -#endif - -ssize_t unwind_backtrace_thread(pid_t tid, backtrace_frame_t* backtrace, - size_t ignore_depth, size_t max_depth) { - if (tid == gettid()) { - return unwind_backtrace(backtrace, ignore_depth + 1, max_depth); - } - - ALOGV("Unwinding thread %d from thread %d.", tid, gettid()); - - // TODO: there's no tgkill(2) on Mac OS, so we'd either need the - // mach_port_t or the pthread_t rather than the tid. -#if defined(CORKSCREW_HAVE_ARCH) && !defined(__APPLE__) - struct sigaction act; - struct sigaction oact; - memset(&act, 0, sizeof(act)); - act.sa_sigaction = unwind_backtrace_thread_signal_handler; - act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK; - sigemptyset(&act.sa_mask); - - pthread_mutex_lock(&g_unwind_signal_mutex); - map_info_t* milist = acquire_my_map_info_list(); - - ssize_t frames = -1; - if (!sigaction(SIGURG, &act, &oact)) { - g_unwind_signal_state.map_info_list = milist; - g_unwind_signal_state.backtrace = backtrace; - g_unwind_signal_state.ignore_depth = ignore_depth; - g_unwind_signal_state.max_depth = max_depth; - g_unwind_signal_state.returned_frames = 0; - android_atomic_release_store(tid, &g_unwind_signal_state.tid_state); - - // Signal the specific thread that we want to dump. - int32_t tid_state = tid; - if (tgkill(getpid(), tid, SIGURG)) { - ALOGV("Failed to send SIGURG to thread %d.", tid); - } else { - // Wait for the other thread to start dumping the stack, or time out. - int wait_millis = 250; - for (;;) { - tid_state = android_atomic_acquire_load(&g_unwind_signal_state.tid_state); - if (tid_state != tid) { - break; - } - if (wait_millis--) { - ALOGV("Waiting for thread %d to start dumping the stack...", tid); - usleep(1000); - } else { - ALOGV("Timed out waiting for thread %d to start dumping the stack.", tid); - break; - } - } - } - - // Try to cancel the dump if it has not started yet. - if (tid_state == tid) { - if (!android_atomic_acquire_cas(tid, STATE_CANCEL, &g_unwind_signal_state.tid_state)) { - ALOGV("Canceled thread %d stack dump.", tid); - tid_state = STATE_CANCEL; - } else { - tid_state = android_atomic_acquire_load(&g_unwind_signal_state.tid_state); - } - } - - // Wait indefinitely for the dump to finish or be canceled. - // We cannot apply a timeout here because the other thread is accessing state that - // is owned by this thread, such as milist. It should not take very - // long to take the dump once started. - while (tid_state == STATE_DUMPING) { - ALOGV("Waiting for thread %d to finish dumping the stack...", tid); - usleep(1000); - tid_state = android_atomic_acquire_load(&g_unwind_signal_state.tid_state); - } - - if (tid_state == STATE_DONE) { - frames = g_unwind_signal_state.returned_frames; - } - - sigaction(SIGURG, &oact, NULL); - } - - release_my_map_info_list(milist); - pthread_mutex_unlock(&g_unwind_signal_mutex); - return frames; -#else - return -1; -#endif -} - -ssize_t unwind_backtrace_ptrace(pid_t tid, const ptrace_context_t* context, - backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth) { -#ifdef CORKSCREW_HAVE_ARCH - return unwind_backtrace_ptrace_arch(tid, context, backtrace, ignore_depth, max_depth); -#else - return -1; -#endif -} - -static void init_backtrace_symbol(backtrace_symbol_t* symbol, uintptr_t pc) { - symbol->relative_pc = pc; - symbol->relative_symbol_addr = 0; - symbol->map_name = NULL; - symbol->symbol_name = NULL; - symbol->demangled_name = NULL; -} - -void get_backtrace_symbols(const backtrace_frame_t* backtrace, size_t frames, - backtrace_symbol_t* backtrace_symbols) { - map_info_t* milist = acquire_my_map_info_list(); - for (size_t i = 0; i < frames; i++) { - const backtrace_frame_t* frame = &backtrace[i]; - backtrace_symbol_t* symbol = &backtrace_symbols[i]; - init_backtrace_symbol(symbol, frame->absolute_pc); - - const map_info_t* mi = find_map_info(milist, frame->absolute_pc); - if (mi) { - symbol->relative_pc = frame->absolute_pc - mi->start; - if (mi->name[0]) { - symbol->map_name = strdup(mi->name); - } - Dl_info info; - if (dladdr((const void*)frame->absolute_pc, &info) && info.dli_sname) { - symbol->relative_symbol_addr = (uintptr_t)info.dli_saddr - - (uintptr_t)info.dli_fbase; - symbol->symbol_name = strdup(info.dli_sname); - symbol->demangled_name = demangle_symbol_name(symbol->symbol_name); - } - } - } - release_my_map_info_list(milist); -} - -void get_backtrace_symbols_ptrace(const ptrace_context_t* context, - const backtrace_frame_t* backtrace, size_t frames, - backtrace_symbol_t* backtrace_symbols) { - for (size_t i = 0; i < frames; i++) { - const backtrace_frame_t* frame = &backtrace[i]; - backtrace_symbol_t* symbol = &backtrace_symbols[i]; - init_backtrace_symbol(symbol, frame->absolute_pc); - - const map_info_t* mi; - const symbol_t* s; - find_symbol_ptrace(context, frame->absolute_pc, &mi, &s); - if (mi) { - symbol->relative_pc = frame->absolute_pc - mi->start; - if (mi->name[0]) { - symbol->map_name = strdup(mi->name); - } - } - if (s) { - symbol->relative_symbol_addr = s->start; - symbol->symbol_name = strdup(s->name); - symbol->demangled_name = demangle_symbol_name(symbol->symbol_name); - } - } -} - -void free_backtrace_symbols(backtrace_symbol_t* backtrace_symbols, size_t frames) { - for (size_t i = 0; i < frames; i++) { - backtrace_symbol_t* symbol = &backtrace_symbols[i]; - free(symbol->map_name); - free(symbol->symbol_name); - free(symbol->demangled_name); - init_backtrace_symbol(symbol, 0); - } -} - -void format_backtrace_line(unsigned frameNumber, const backtrace_frame_t* frame __attribute__((unused)), - const backtrace_symbol_t* symbol, char* buffer, size_t bufferSize) { - const char* mapName = symbol->map_name ? symbol->map_name : "<unknown>"; - const char* symbolName = symbol->demangled_name ? symbol->demangled_name : symbol->symbol_name; - int fieldWidth = (bufferSize - 80) / 2; - if (symbolName) { - uint32_t pc_offset = symbol->relative_pc - symbol->relative_symbol_addr; - if (pc_offset) { - snprintf(buffer, bufferSize, "#%02u pc %08x %.*s (%.*s+%u)", - frameNumber, (unsigned int) symbol->relative_pc, - fieldWidth, mapName, fieldWidth, symbolName, pc_offset); - } else { - snprintf(buffer, bufferSize, "#%02u pc %08x %.*s (%.*s)", - frameNumber, (unsigned int) symbol->relative_pc, - fieldWidth, mapName, fieldWidth, symbolName); - } - } else { - snprintf(buffer, bufferSize, "#%02u pc %08x %.*s", - frameNumber, (unsigned int) symbol->relative_pc, - fieldWidth, mapName); - } -} diff --git a/libcorkscrew/demangle.c b/libcorkscrew/demangle.c deleted file mode 100644 index 30ab1b0..0000000 --- a/libcorkscrew/demangle.c +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -#define LOG_TAG "Corkscrew" -//#define LOG_NDEBUG 0 - -#include <corkscrew/demangle.h> - -#include <cutils/log.h> - -extern char *__cxa_demangle (const char *mangled, char *buf, size_t *len, - int *status); - -char* demangle_symbol_name(const char* name) { -#if defined(__APPLE__) - // Mac OS' __cxa_demangle demangles "f" as "float"; last tested on 10.7. - if (name != NULL && name[0] != '_') { - return NULL; - } -#endif - // __cxa_demangle handles NULL by returning NULL - return __cxa_demangle(name, 0, 0, 0); -} diff --git a/libcorkscrew/map_info.c b/libcorkscrew/map_info.c deleted file mode 100644 index 93dffbf..0000000 --- a/libcorkscrew/map_info.c +++ /dev/null @@ -1,279 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -#define LOG_TAG "Corkscrew" -//#define LOG_NDEBUG 0 - -#include <corkscrew/map_info.h> - -#include <ctype.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <limits.h> -#include <pthread.h> -#include <unistd.h> -#include <cutils/log.h> -#include <sys/time.h> - -#if defined(__APPLE__) - -// Mac OS vmmap(1) output: -// __TEXT 0009f000-000a1000 [ 8K 8K] r-x/rwx SM=COW /Volumes/android/dalvik-dev/out/host/darwin-x86/bin/libcorkscrew_test\n -// 012345678901234567890123456789012345678901234567890123456789 -// 0 1 2 3 4 5 -static map_info_t* parse_vmmap_line(const char* line) { - unsigned long int start; - unsigned long int end; - char permissions[4]; - int name_pos; - if (sscanf(line, "%*21c %lx-%lx [%*13c] %3c/%*3c SM=%*3c %n", - &start, &end, permissions, &name_pos) != 3) { - return NULL; - } - - const char* name = line + name_pos; - size_t name_len = strlen(name); - - map_info_t* mi = calloc(1, sizeof(map_info_t) + name_len); - if (mi != NULL) { - mi->start = start; - mi->end = end; - mi->is_readable = permissions[0] == 'r'; - mi->is_writable = permissions[1] == 'w'; - mi->is_executable = permissions[2] == 'x'; - mi->data = NULL; - memcpy(mi->name, name, name_len); - mi->name[name_len - 1] = '\0'; - ALOGV("Parsed map: start=0x%08x, end=0x%08x, " - "is_readable=%d, is_writable=%d is_executable=%d, name=%s", - mi->start, mi->end, - mi->is_readable, mi->is_writable, mi->is_executable, mi->name); - } - return mi; -} - -map_info_t* load_map_info_list(pid_t pid) { - char cmd[1024]; - snprintf(cmd, sizeof(cmd), "vmmap -w -resident -submap -allSplitLibs -interleaved %d", pid); - FILE* fp = popen(cmd, "r"); - if (fp == NULL) { - return NULL; - } - - char line[1024]; - map_info_t* milist = NULL; - while (fgets(line, sizeof(line), fp) != NULL) { - map_info_t* mi = parse_vmmap_line(line); - if (mi != NULL) { - mi->next = milist; - milist = mi; - } - } - pclose(fp); - return milist; -} - -#else - -// Linux /proc/<pid>/maps lines: -// 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so\n -// 012345678901234567890123456789012345678901234567890123456789 -// 0 1 2 3 4 5 -static map_info_t* parse_maps_line(const char* line) -{ - unsigned long int start; - unsigned long int end; - char permissions[5]; - int name_pos; - if (sscanf(line, "%lx-%lx %4s %*x %*x:%*x %*d%n", &start, &end, - permissions, &name_pos) != 3) { - return NULL; - } - - while (isspace(line[name_pos])) { - name_pos += 1; - } - const char* name = line + name_pos; - size_t name_len = strlen(name); - if (name_len && name[name_len - 1] == '\n') { - name_len -= 1; - } - - map_info_t* mi = calloc(1, sizeof(map_info_t) + name_len + 1); - if (mi) { - mi->start = start; - mi->end = end; - mi->is_readable = strlen(permissions) == 4 && permissions[0] == 'r'; - mi->is_writable = strlen(permissions) == 4 && permissions[1] == 'w'; - mi->is_executable = strlen(permissions) == 4 && permissions[2] == 'x'; - mi->data = NULL; - memcpy(mi->name, name, name_len); - mi->name[name_len] = '\0'; - ALOGV("Parsed map: start=0x%08x, end=0x%08x, " - "is_readable=%d, is_writable=%d, is_executable=%d, name=%s", - mi->start, mi->end, - mi->is_readable, mi->is_writable, mi->is_executable, mi->name); - } - return mi; -} - -map_info_t* load_map_info_list(pid_t tid) { - char path[PATH_MAX]; - char line[1024]; - FILE* fp; - map_info_t* milist = NULL; - - snprintf(path, PATH_MAX, "/proc/%d/maps", tid); - fp = fopen(path, "r"); - if (fp) { - while(fgets(line, sizeof(line), fp)) { - map_info_t* mi = parse_maps_line(line); - if (mi) { - mi->next = milist; - milist = mi; - } - } - fclose(fp); - } - return milist; -} - -#endif - -void free_map_info_list(map_info_t* milist) { - while (milist) { - map_info_t* next = milist->next; - free(milist); - milist = next; - } -} - -const map_info_t* find_map_info(const map_info_t* milist, uintptr_t addr) { - const map_info_t* mi = milist; - while (mi && !(addr >= mi->start && addr < mi->end)) { - mi = mi->next; - } - return mi; -} - -bool is_readable_map(const map_info_t* milist, uintptr_t addr) { - const map_info_t* mi = find_map_info(milist, addr); - return mi && mi->is_readable; -} - -bool is_writable_map(const map_info_t* milist, uintptr_t addr) { - const map_info_t* mi = find_map_info(milist, addr); - return mi && mi->is_writable; -} - -bool is_executable_map(const map_info_t* milist, uintptr_t addr) { - const map_info_t* mi = find_map_info(milist, addr); - return mi && mi->is_executable; -} - -static pthread_mutex_t g_my_map_info_list_mutex = PTHREAD_MUTEX_INITIALIZER; -static map_info_t* g_my_map_info_list = NULL; - -static const int64_t MAX_CACHE_AGE = 5 * 1000 * 1000000LL; - -typedef struct { - uint32_t refs; - int64_t timestamp; -} my_map_info_data_t; - -static int64_t now_ns() { -#if defined(HAVE_POSIX_CLOCKS) - struct timespec t; - t.tv_sec = t.tv_nsec = 0; - clock_gettime(CLOCK_MONOTONIC, &t); - return t.tv_sec * 1000000000LL + t.tv_nsec; -#else - struct timeval t; - gettimeofday(&t, NULL); - return t.tv_sec * 1000000000LL + t.tv_usec * 1000LL; -#endif -} - -static void dec_ref(map_info_t* milist, my_map_info_data_t* data) { - if (!--data->refs) { - ALOGV("Freed my_map_info_list %p.", milist); - free(data); - free_map_info_list(milist); - } -} - -map_info_t* acquire_my_map_info_list() { - pthread_mutex_lock(&g_my_map_info_list_mutex); - - int64_t time = now_ns(); - if (g_my_map_info_list != NULL) { - my_map_info_data_t* data = (my_map_info_data_t*)g_my_map_info_list->data; - int64_t age = time - data->timestamp; - if (age >= MAX_CACHE_AGE) { - ALOGV("Invalidated my_map_info_list %p, age=%lld.", g_my_map_info_list, age); - dec_ref(g_my_map_info_list, data); - g_my_map_info_list = NULL; - } else { - ALOGV("Reusing my_map_info_list %p, age=%lld.", g_my_map_info_list, age); - } - } - - if (g_my_map_info_list == NULL) { - my_map_info_data_t* data = (my_map_info_data_t*)malloc(sizeof(my_map_info_data_t)); - g_my_map_info_list = load_map_info_list(getpid()); - if (g_my_map_info_list != NULL) { - ALOGV("Loaded my_map_info_list %p.", g_my_map_info_list); - g_my_map_info_list->data = data; - data->refs = 1; - data->timestamp = time; - } else { - free(data); - } - } - - map_info_t* milist = g_my_map_info_list; - if (milist) { - my_map_info_data_t* data = (my_map_info_data_t*)g_my_map_info_list->data; - data->refs += 1; - } - - pthread_mutex_unlock(&g_my_map_info_list_mutex); - return milist; -} - -void release_my_map_info_list(map_info_t* milist) { - if (milist) { - pthread_mutex_lock(&g_my_map_info_list_mutex); - - my_map_info_data_t* data = (my_map_info_data_t*)milist->data; - dec_ref(milist, data); - - pthread_mutex_unlock(&g_my_map_info_list_mutex); - } -} - -void flush_my_map_info_list() { - pthread_mutex_lock(&g_my_map_info_list_mutex); - - if (g_my_map_info_list != NULL) { - my_map_info_data_t* data = (my_map_info_data_t*) g_my_map_info_list->data; - dec_ref(g_my_map_info_list, data); - g_my_map_info_list = NULL; - } - - pthread_mutex_unlock(&g_my_map_info_list_mutex); -} diff --git a/libcorkscrew/ptrace-arch.h b/libcorkscrew/ptrace-arch.h deleted file mode 100755 index 0bcff63..0000000 --- a/libcorkscrew/ptrace-arch.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -/* Architecture dependent functions. */ - -#ifndef _CORKSCREW_PTRACE_ARCH_H -#define _CORKSCREW_PTRACE_ARCH_H - -#include <corkscrew/ptrace.h> -#include <corkscrew/map_info.h> -#include <corkscrew/symbol_table.h> - -#ifdef __cplusplus -extern "C" { -#endif - -/* Custom extra data we stuff into map_info_t structures as part - * of our ptrace_context_t. */ -typedef struct { -#ifdef __arm__ - uintptr_t exidx_start; - size_t exidx_size; -#elif __mips__ - uintptr_t eh_frame_hdr; -#elif __i386__ - uintptr_t eh_frame_hdr; -#endif - symbol_table_t* symbol_table; -} map_info_data_t; - -void load_ptrace_map_info_data_arch(pid_t pid, map_info_t* mi, map_info_data_t* data); -void free_ptrace_map_info_data_arch(map_info_t* mi, map_info_data_t* data); - -#ifdef __cplusplus -} -#endif - -#endif // _CORKSCREW_PTRACE_ARCH_H diff --git a/libcorkscrew/ptrace.c b/libcorkscrew/ptrace.c deleted file mode 100644 index be58f7f..0000000 --- a/libcorkscrew/ptrace.c +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -#define LOG_TAG "Corkscrew" -//#define LOG_NDEBUG 0 - -#include "ptrace-arch.h" -#include <corkscrew/ptrace.h> - -#include <errno.h> -#include <stdlib.h> -#include <sys/ptrace.h> -#include <cutils/log.h> - -static const uint32_t ELF_MAGIC = 0x464C457f; // "ELF\0177" - -#ifndef PAGE_SIZE -#define PAGE_SIZE 4096 -#endif - -#ifndef PAGE_MASK -#define PAGE_MASK (~(PAGE_SIZE - 1)) -#endif - -void init_memory(memory_t* memory, const map_info_t* map_info_list) { - memory->tid = -1; - memory->map_info_list = map_info_list; -} - -void init_memory_ptrace(memory_t* memory, pid_t tid) { - memory->tid = tid; - memory->map_info_list = NULL; -} - -bool try_get_word(const memory_t* memory, uintptr_t ptr, uint32_t* out_value) { - ALOGV("try_get_word: reading word at %p", (void*) ptr); - if (ptr & 3) { - ALOGV("try_get_word: invalid pointer %p", (void*) ptr); - *out_value = 0xffffffffL; - return false; - } - if (memory->tid < 0) { - if (!is_readable_map(memory->map_info_list, ptr)) { - ALOGV("try_get_word: pointer %p not in a readable map", (void*) ptr); - *out_value = 0xffffffffL; - return false; - } - *out_value = *(uint32_t*)ptr; - return true; - } else { -#if defined(__APPLE__) - ALOGV("no ptrace on Mac OS"); - return false; -#else - // ptrace() returns -1 and sets errno when the operation fails. - // To disambiguate -1 from a valid result, we clear errno beforehand. - errno = 0; - *out_value = ptrace(PTRACE_PEEKTEXT, memory->tid, (void*)ptr, NULL); - if (*out_value == 0xffffffffL && errno) { - ALOGV("try_get_word: invalid pointer 0x%08x reading from tid %d, " - "ptrace() errno=%d", ptr, memory->tid, errno); - return false; - } - return true; -#endif - } -} - -bool try_get_word_ptrace(pid_t tid, uintptr_t ptr, uint32_t* out_value) { - memory_t memory; - init_memory_ptrace(&memory, tid); - return try_get_word(&memory, ptr, out_value); -} - -static void load_ptrace_map_info_data(pid_t pid, map_info_t* mi) { - if (mi->is_executable && mi->is_readable) { - uint32_t elf_magic; - if (try_get_word_ptrace(pid, mi->start, &elf_magic) && elf_magic == ELF_MAGIC) { - map_info_data_t* data = (map_info_data_t*)calloc(1, sizeof(map_info_data_t)); - if (data) { - mi->data = data; - if (mi->name[0]) { - data->symbol_table = load_symbol_table(mi->name); - } -#ifdef CORKSCREW_HAVE_ARCH - load_ptrace_map_info_data_arch(pid, mi, data); -#endif - } - } - } -} - -ptrace_context_t* load_ptrace_context(pid_t pid) { - ptrace_context_t* context = - (ptrace_context_t*)calloc(1, sizeof(ptrace_context_t)); - if (context) { - context->map_info_list = load_map_info_list(pid); - for (map_info_t* mi = context->map_info_list; mi; mi = mi->next) { - load_ptrace_map_info_data(pid, mi); - } - } - return context; -} - -static void free_ptrace_map_info_data(map_info_t* mi) { - map_info_data_t* data = (map_info_data_t*)mi->data; - if (data) { - if (data->symbol_table) { - free_symbol_table(data->symbol_table); - } -#ifdef CORKSCREW_HAVE_ARCH - free_ptrace_map_info_data_arch(mi, data); -#endif - free(data); - mi->data = NULL; - } -} - -void free_ptrace_context(ptrace_context_t* context) { - for (map_info_t* mi = context->map_info_list; mi; mi = mi->next) { - free_ptrace_map_info_data(mi); - } - free_map_info_list(context->map_info_list); - free(context); -} - -void find_symbol_ptrace(const ptrace_context_t* context, - uintptr_t addr, const map_info_t** out_map_info, const symbol_t** out_symbol) { - const map_info_t* mi = find_map_info(context->map_info_list, addr); - const symbol_t* symbol = NULL; - if (mi) { - const map_info_data_t* data = (const map_info_data_t*)mi->data; - if (data && data->symbol_table) { - symbol = find_symbol(data->symbol_table, addr - mi->start); - } - } - *out_map_info = mi; - *out_symbol = symbol; -} diff --git a/libcorkscrew/symbol_table.c b/libcorkscrew/symbol_table.c deleted file mode 100644 index 982ccc8..0000000 --- a/libcorkscrew/symbol_table.c +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -#define LOG_TAG "Corkscrew" -//#define LOG_NDEBUG 0 - -#include <corkscrew/symbol_table.h> - -#include <stdbool.h> -#include <stdlib.h> -#include <fcntl.h> -#include <string.h> -#include <sys/stat.h> -#include <sys/mman.h> -#include <cutils/log.h> - -#if defined(__APPLE__) -#else - -#include <elf.h> - -static bool is_elf(Elf32_Ehdr* e) { - return (e->e_ident[EI_MAG0] == ELFMAG0 && - e->e_ident[EI_MAG1] == ELFMAG1 && - e->e_ident[EI_MAG2] == ELFMAG2 && - e->e_ident[EI_MAG3] == ELFMAG3); -} - -#endif - -// Compare function for qsort -static int qcompar(const void *a, const void *b) { - const symbol_t* asym = (const symbol_t*)a; - const symbol_t* bsym = (const symbol_t*)b; - if (asym->start > bsym->start) return 1; - if (asym->start < bsym->start) return -1; - return 0; -} - -// Compare function for bsearch -static int bcompar(const void *key, const void *element) { - uintptr_t addr = *(const uintptr_t*)key; - const symbol_t* symbol = (const symbol_t*)element; - if (addr < symbol->start) return -1; - if (addr >= symbol->end) return 1; - return 0; -} - -symbol_table_t* load_symbol_table(const char *filename) { - symbol_table_t* table = NULL; -#if !defined(__APPLE__) - ALOGV("Loading symbol table from '%s'.", filename); - - int fd = open(filename, O_RDONLY); - if (fd < 0) { - goto out; - } - - struct stat sb; - if (fstat(fd, &sb)) { - goto out_close; - } - - size_t length = sb.st_size; - char* base = mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, 0); - if (base == MAP_FAILED) { - goto out_close; - } - - // Parse the file header - Elf32_Ehdr *hdr = (Elf32_Ehdr*)base; - if (!is_elf(hdr)) { - goto out_close; - } - Elf32_Shdr *shdr = (Elf32_Shdr*)(base + hdr->e_shoff); - - // Search for the dynamic symbols section - int sym_idx = -1; - int dynsym_idx = -1; - for (Elf32_Half i = 0; i < hdr->e_shnum; i++) { - if (shdr[i].sh_type == SHT_SYMTAB) { - sym_idx = i; - } - if (shdr[i].sh_type == SHT_DYNSYM) { - dynsym_idx = i; - } - } - if (dynsym_idx == -1 && sym_idx == -1) { - goto out_unmap; - } - - table = malloc(sizeof(symbol_table_t)); - if(!table) { - goto out_unmap; - } - table->num_symbols = 0; - - Elf32_Sym *dynsyms = NULL; - int dynnumsyms = 0; - char *dynstr = NULL; - if (dynsym_idx != -1) { - dynsyms = (Elf32_Sym*)(base + shdr[dynsym_idx].sh_offset); - dynnumsyms = shdr[dynsym_idx].sh_size / shdr[dynsym_idx].sh_entsize; - int dynstr_idx = shdr[dynsym_idx].sh_link; - dynstr = base + shdr[dynstr_idx].sh_offset; - } - - Elf32_Sym *syms = NULL; - int numsyms = 0; - char *str = NULL; - if (sym_idx != -1) { - syms = (Elf32_Sym*)(base + shdr[sym_idx].sh_offset); - numsyms = shdr[sym_idx].sh_size / shdr[sym_idx].sh_entsize; - int str_idx = shdr[sym_idx].sh_link; - str = base + shdr[str_idx].sh_offset; - } - - int dynsymbol_count = 0; - if (dynsym_idx != -1) { - // Iterate through the dynamic symbol table, and count how many symbols - // are actually defined - for (int i = 0; i < dynnumsyms; i++) { - if (dynsyms[i].st_shndx != SHN_UNDEF) { - dynsymbol_count++; - } - } - } - - size_t symbol_count = 0; - if (sym_idx != -1) { - // Iterate through the symbol table, and count how many symbols - // are actually defined - for (int i = 0; i < numsyms; i++) { - if (syms[i].st_shndx != SHN_UNDEF - && str[syms[i].st_name] - && syms[i].st_value - && syms[i].st_size) { - symbol_count++; - } - } - } - - // Now, create an entry in our symbol table structure for each symbol... - table->num_symbols += symbol_count + dynsymbol_count; - table->symbols = malloc(table->num_symbols * sizeof(symbol_t)); - if (!table->symbols) { - free(table); - table = NULL; - goto out_unmap; - } - - size_t symbol_index = 0; - if (dynsym_idx != -1) { - // ...and populate them - for (int i = 0; i < dynnumsyms; i++) { - if (dynsyms[i].st_shndx != SHN_UNDEF) { - table->symbols[symbol_index].name = strdup(dynstr + dynsyms[i].st_name); - table->symbols[symbol_index].start = dynsyms[i].st_value; - table->symbols[symbol_index].end = dynsyms[i].st_value + dynsyms[i].st_size; - ALOGV(" [%d] '%s' 0x%08x-0x%08x (DYNAMIC)", - symbol_index, table->symbols[symbol_index].name, - table->symbols[symbol_index].start, table->symbols[symbol_index].end); - symbol_index += 1; - } - } - } - - if (sym_idx != -1) { - // ...and populate them - for (int i = 0; i < numsyms; i++) { - if (syms[i].st_shndx != SHN_UNDEF - && str[syms[i].st_name] - && syms[i].st_value - && syms[i].st_size) { - table->symbols[symbol_index].name = strdup(str + syms[i].st_name); - table->symbols[symbol_index].start = syms[i].st_value; - table->symbols[symbol_index].end = syms[i].st_value + syms[i].st_size; - ALOGV(" [%d] '%s' 0x%08x-0x%08x", - symbol_index, table->symbols[symbol_index].name, - table->symbols[symbol_index].start, table->symbols[symbol_index].end); - symbol_index += 1; - } - } - } - - // Sort the symbol table entries, so they can be bsearched later - qsort(table->symbols, table->num_symbols, sizeof(symbol_t), qcompar); - -out_unmap: - munmap(base, length); - -out_close: - close(fd); -#endif - -out: - return table; -} - -void free_symbol_table(symbol_table_t* table) { - if (table) { - for (size_t i = 0; i < table->num_symbols; i++) { - free(table->symbols[i].name); - } - free(table->symbols); - free(table); - } -} - -const symbol_t* find_symbol(const symbol_table_t* table, uintptr_t addr) { - if (!table) return NULL; - return (const symbol_t*)bsearch(&addr, table->symbols, table->num_symbols, - sizeof(symbol_t), bcompar); -} diff --git a/libcorkscrew/test.cpp b/libcorkscrew/test.cpp deleted file mode 100644 index 22dfa7d..0000000 --- a/libcorkscrew/test.cpp +++ /dev/null @@ -1,76 +0,0 @@ -#include <corkscrew/backtrace.h> -#include <corkscrew/symbol_table.h> -#include <stdio.h> -#include <stdlib.h> - -int do_backtrace(float /* just to test demangling */) { - const size_t MAX_DEPTH = 32; - backtrace_frame_t* frames = (backtrace_frame_t*) malloc(sizeof(backtrace_frame_t) * MAX_DEPTH); - ssize_t frame_count = unwind_backtrace(frames, 0, MAX_DEPTH); - fprintf(stderr, "frame_count=%d\n", (int) frame_count); - if (frame_count <= 0) { - return frame_count; - } - - backtrace_symbol_t* backtrace_symbols = (backtrace_symbol_t*) malloc(sizeof(backtrace_symbol_t) * frame_count); - get_backtrace_symbols(frames, frame_count, backtrace_symbols); - - for (size_t i = 0; i < (size_t) frame_count; ++i) { - char line[MAX_BACKTRACE_LINE_LENGTH]; - format_backtrace_line(i, &frames[i], &backtrace_symbols[i], - line, MAX_BACKTRACE_LINE_LENGTH); - if (backtrace_symbols[i].symbol_name != NULL) { - // get_backtrace_symbols found the symbol's name with dladdr(3). - fprintf(stderr, " %s\n", line); - } else { - // We don't have a symbol. Maybe this is a static symbol, and - // we can look it up? - symbol_table_t* symbols = NULL; - if (backtrace_symbols[i].map_name != NULL) { - symbols = load_symbol_table(backtrace_symbols[i].map_name); - } - const symbol_t* symbol = NULL; - if (symbols != NULL) { - symbol = find_symbol(symbols, frames[i].absolute_pc); - } - if (symbol != NULL) { - int offset = frames[i].absolute_pc - symbol->start; - fprintf(stderr, " %s (%s%+d)\n", line, symbol->name, offset); - } else { - fprintf(stderr, " %s (\?\?\?)\n", line); - } - free_symbol_table(symbols); - } - } - - free_backtrace_symbols(backtrace_symbols, frame_count); - free(backtrace_symbols); - free(frames); - return frame_count; -} - -struct C { - int g(int i); -}; - -__attribute__ ((noinline)) int C::g(int i) { - if (i == 0) { - return do_backtrace(0.1); - } - return g(i - 1); -} - -extern "C" __attribute__ ((noinline)) int f() { - C c; - return c.g(5); -} - -int main() { - flush_my_map_info_list(); - f(); - - flush_my_map_info_list(); - f(); - - return 0; -} diff --git a/libctest/Android.mk b/libctest/Android.mk deleted file mode 100644 index 815fabb..0000000 --- a/libctest/Android.mk +++ /dev/null @@ -1,7 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE:= libctest -LOCAL_SRC_FILES := ctest.c - -include $(BUILD_SHARED_LIBRARY) diff --git a/libctest/NOTICE b/libctest/NOTICE deleted file mode 100644 index c5b1efa..0000000 --- a/libctest/NOTICE +++ /dev/null @@ -1,190 +0,0 @@ - - Copyright (c) 2005-2008, 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. - - 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. - - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - diff --git a/libctest/ctest.c b/libctest/ctest.c deleted file mode 100644 index ee6331f..0000000 --- a/libctest/ctest.c +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright (C) 2007 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 <assert.h> -#include <stdlib.h> -#include <stdio.h> -#include <sys/types.h> -#include <sys/wait.h> -#include <unistd.h> -#include <ctest/ctest.h> - -#define MAX_TESTS 255 - -/** Semi-random number used to identify assertion errors. */ -#define ASSERTION_ERROR 42 - -typedef void TestCase(); - -/** A suite of tests. */ -typedef struct { - int size; - const char* testNames[MAX_TESTS]; - TestCase* tests[MAX_TESTS]; - int currentTest; - FILE* out; -} TestSuite; - -/** Gets the test suite. Creates it if necessary. */ -static TestSuite* getTestSuite() { - static TestSuite* suite = NULL; - - if (suite != NULL) { - return suite; - } - - suite = calloc(1, sizeof(TestSuite)); - assert(suite != NULL); - - suite->out = tmpfile(); - assert(suite->out != NULL); - - return suite; -} - -void addNamedTest(const char* name, TestCase* test) { - TestSuite* testSuite = getTestSuite(); - assert(testSuite->size <= MAX_TESTS); - - int index = testSuite->size; - testSuite->testNames[index] = name; - testSuite->tests[index] = test; - - testSuite->size++; -} - -/** Prints failures to stderr. */ -static void printFailures(int failures) { - TestSuite* suite = getTestSuite(); - - fprintf(stderr, "FAILURE! %d of %d tests failed. Failures:\n", - failures, suite->size); - - // Copy test output to stdout. - rewind(suite->out); - char buffer[512]; - size_t read; - while ((read = fread(buffer, sizeof(char), 512, suite->out)) > 0) { - // TODO: Make sure we actually wrote 'read' bytes. - fwrite(buffer, sizeof(char), read, stderr); - } -} - -/** Runs a single test case. */ -static int runCurrentTest() { - TestSuite* suite = getTestSuite(); - - pid_t pid = fork(); - if (pid == 0) { - // Child process. Runs test case. - suite->tests[suite->currentTest](); - - // Exit successfully. - exit(0); - } else if (pid < 0) { - fprintf(stderr, "Fork failed."); - exit(1); - } else { - // Parent process. Wait for child. - int status; - waitpid(pid, &status, 0); - - if (!WIFEXITED(status)) { - return -1; - } - - return WEXITSTATUS(status); - } -} - -void runTests() { - TestSuite* suite = getTestSuite(); - - int failures = 0; - for (suite->currentTest = 0; suite->currentTest < suite->size; - suite->currentTest++) { - // Flush stdout before forking. - fflush(stdout); - - int result = runCurrentTest(); - - if (result != 0) { - printf("X"); - - failures++; - - // Handle errors other than assertions. - if (result != ASSERTION_ERROR) { - // TODO: Report file name. - fprintf(suite->out, "Process failed: [%s] status: %d\n", - suite->testNames[suite->currentTest], result); - fflush(suite->out); - } - } else { - printf("."); - } - } - - printf("\n"); - - if (failures > 0) { - printFailures(failures); - } else { - printf("SUCCESS! %d tests ran successfully.\n", suite->size); - } -} - -void assertTrueWithSource(int value, const char* file, int line, char* message) { - if (!value) { - TestSuite* suite = getTestSuite(); - - fprintf(suite->out, "Assertion failed: [%s:%d] %s: %s\n", file, line, - suite->testNames[suite->currentTest], message); - fflush(suite->out); - - // Exit the process for this test case. - exit(ASSERTION_ERROR); - } -} diff --git a/libcutils/Android.mk b/libcutils/Android.mk index 93bccb0..933a77b 100644 --- a/libcutils/Android.mk +++ b/libcutils/Android.mk @@ -27,13 +27,6 @@ commonSources := \ hashmap.c \ atomic.c.arm \ native_handle.c \ - socket_inaddr_any_server.c \ - socket_local_client.c \ - socket_local_server.c \ - socket_loopback_client.c \ - socket_loopback_server.c \ - socket_network_client.c \ - sockets.c \ config_utils.c \ cpu_info.c \ load_file.c \ @@ -47,9 +40,6 @@ commonSources := \ iosched_policy.c \ str_parms.c \ -commonHostSources := \ - ashmem-host.c - # some files must not be compiled when building against Mingw # they correspond to features not used by our host development tools # which are also hard or even impossible to port to native Win32 @@ -67,7 +57,18 @@ endif ifneq ($(WINDOWS_HOST_ONLY),1) commonSources += \ fs.c \ - multiuser.c + multiuser.c \ + socket_inaddr_any_server.c \ + socket_local_client.c \ + socket_local_server.c \ + socket_loopback_client.c \ + socket_loopback_server.c \ + socket_network_client.c \ + sockets.c \ + + commonHostSources += \ + ashmem-host.c + endif @@ -75,9 +76,13 @@ endif # ======================================================== LOCAL_MODULE := libcutils LOCAL_SRC_FILES := $(commonSources) $(commonHostSources) dlmalloc_stubs.c -LOCAL_LDLIBS := -lpthread LOCAL_STATIC_LIBRARIES := liblog LOCAL_CFLAGS += $(hostSmpFlag) +ifneq ($(HOST_OS),windows) +LOCAL_CFLAGS += -Werror +endif +LOCAL_MULTILIB := both +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk include $(BUILD_HOST_STATIC_LIBRARY) @@ -86,11 +91,28 @@ include $(BUILD_HOST_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := lib64cutils LOCAL_SRC_FILES := $(commonSources) $(commonHostSources) dlmalloc_stubs.c -LOCAL_LDLIBS := -lpthread LOCAL_STATIC_LIBRARIES := lib64log LOCAL_CFLAGS += $(hostSmpFlag) -m64 +ifneq ($(HOST_OS),windows) +LOCAL_CFLAGS += -Werror +endif +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk include $(BUILD_HOST_STATIC_LIBRARY) +# Tests for host +# ======================================================== +include $(CLEAR_VARS) +LOCAL_MODULE := tst_str_parms +LOCAL_CFLAGS += -DTEST_STR_PARMS +ifneq ($(HOST_OS),windows) +LOCAL_CFLAGS += -Werror +endif +LOCAL_SRC_FILES := str_parms.c hashmap.c memory.c +LOCAL_STATIC_LIBRARIES := liblog +LOCAL_MODULE_TAGS := optional +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk +include $(BUILD_HOST_EXECUTABLE) + # Shared and static library for target # ======================================================== @@ -102,30 +124,40 @@ LOCAL_SRC_FILES := $(commonSources) \ ashmem-dev.c \ debugger.c \ klog.c \ + memory.c \ partition_utils.c \ properties.c \ qtaguid.c \ trace.c \ - uevent.c - -ifeq ($(TARGET_ARCH),arm) - LOCAL_SRC_FILES += arch-arm/memset32.S -else # !arm - ifeq ($(TARGET_ARCH),x86) - LOCAL_CFLAGS += -DHAVE_MEMSET16 -DHAVE_MEMSET32 - LOCAL_SRC_FILES += arch-x86/android_memset16.S arch-x86/android_memset32.S memory.c - else # !x86 - ifeq ($(TARGET_ARCH),mips) - LOCAL_SRC_FILES += arch-mips/android_memset.c - else # !mips - LOCAL_SRC_FILES += memory.c - endif # !mips - endif # !x86 -endif # !arm + uevent.c \ + +LOCAL_SRC_FILES_arm += \ + arch-arm/memset32.S \ + +LOCAL_SRC_FILES_arm64 += \ + arch-arm64/android_memset.S \ + +LOCAL_SRC_FILES_mips += \ + arch-mips/android_memset.c \ + +LOCAL_SRC_FILES_x86 += \ + arch-x86/android_memset16.S \ + arch-x86/android_memset32.S \ + +LOCAL_SRC_FILES_x86_64 += \ + arch-x86_64/android_memset16_SSE2-atom.S \ + arch-x86_64/android_memset32_SSE2-atom.S \ + +LOCAL_CFLAGS_arm += -DHAVE_MEMSET16 -DHAVE_MEMSET32 +LOCAL_CFLAGS_arm64 += -DHAVE_MEMSET16 -DHAVE_MEMSET32 +LOCAL_CFLAGS_mips += -DHAVE_MEMSET16 -DHAVE_MEMSET32 +LOCAL_CFLAGS_x86 += -DHAVE_MEMSET16 -DHAVE_MEMSET32 +LOCAL_CFLAGS_x86_64 += -DHAVE_MEMSET16 -DHAVE_MEMSET32 LOCAL_C_INCLUDES := $(libcutils_c_includes) LOCAL_STATIC_LIBRARIES := liblog -LOCAL_CFLAGS += $(targetSmpFlag) +LOCAL_CFLAGS += $(targetSmpFlag) -Werror +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) @@ -134,16 +166,18 @@ LOCAL_MODULE := libcutils # liblog symbols present in libcutils. LOCAL_WHOLE_STATIC_LIBRARIES := libcutils liblog LOCAL_SHARED_LIBRARIES := liblog -LOCAL_CFLAGS += $(targetSmpFlag) +LOCAL_CFLAGS += $(targetSmpFlag) -Werror LOCAL_C_INCLUDES := $(libcutils_c_includes) +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk include $(BUILD_SHARED_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := tst_str_parms -LOCAL_CFLAGS += -DTEST_STR_PARMS +LOCAL_CFLAGS += -DTEST_STR_PARMS -Werror LOCAL_SRC_FILES := str_parms.c hashmap.c memory.c LOCAL_SHARED_LIBRARIES := liblog LOCAL_MODULE_TAGS := optional +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk include $(BUILD_EXECUTABLE) include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/libcutils/android_reboot.c b/libcutils/android_reboot.c index b7895fa..5d98295 100644 --- a/libcutils/android_reboot.c +++ b/libcutils/android_reboot.c @@ -57,7 +57,7 @@ static int remount_ro_done(void) mount_dir[255] = 0; mount_type[255] = 0; mount_opts[255] = 0; - if ((match == 6) && !strncmp(mount_dev, "/dev/block", 10) && strstr(mount_opts, "rw")) { + if ((match == 6) && !strncmp(mount_dev, "/dev/block", 10) && strstr(mount_opts, "rw,")) { found_rw_fs = 1; break; } diff --git a/libcutils/arch-arm/memset32.S b/libcutils/arch-arm/memset32.S index 4697265..6efab9f 100644 --- a/libcutils/arch-arm/memset32.S +++ b/libcutils/arch-arm/memset32.S @@ -51,8 +51,10 @@ android_memset16: android_memset32: .fnstart - .save {lr} + .cfi_startproc str lr, [sp, #-4]! + .cfi_def_cfa_offset 4 + .cfi_rel_offset lr, 0 /* align the destination to a cache-line */ mov r12, r1 @@ -89,5 +91,8 @@ android_memset32: strmih lr, [r0], #2 ldr lr, [sp], #4 + .cfi_def_cfa_offset 0 + .cfi_restore lr bx lr + .cfi_endproc .fnend diff --git a/libcutils/arch-arm64/android_memset.S b/libcutils/arch-arm64/android_memset.S new file mode 100644 index 0000000..9a83a68 --- /dev/null +++ b/libcutils/arch-arm64/android_memset.S @@ -0,0 +1,211 @@ +/* Copyright (c) 2012, Linaro Limited + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Linaro nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* Assumptions: + * + * ARMv8-a, AArch64 + * Unaligned accesses + * + */ + +/* By default we assume that the DC instruction can be used to zero + data blocks more efficiently. In some circumstances this might be + unsafe, for example in an asymmetric multiprocessor environment with + different DC clear lengths (neither the upper nor lower lengths are + safe to use). */ + +#define dst x0 +#define count x2 +#define tmp1 x3 +#define tmp1w w3 +#define tmp2 x4 +#define tmp2w w4 +#define zva_len_x x5 +#define zva_len w5 +#define zva_bits_x x6 + +#define A_l x1 +#define A_lw w1 +#define tmp3w w9 + +#define ENTRY(f) \ + .text; \ + .globl f; \ + .align 0; \ + .type f, %function; \ + f: \ + .cfi_startproc \ + +#define END(f) \ + .cfi_endproc; \ + .size f, .-f; \ + +ENTRY(android_memset16) + ands A_lw, A_lw, #0xffff + b.eq .Lzero_mem + orr A_lw, A_lw, A_lw, lsl #16 + b .Lexpand_to_64 +END(android_memset16) + +ENTRY(android_memset32) + cmp A_lw, #0 + b.eq .Lzero_mem +.Lexpand_to_64: + orr A_l, A_l, A_l, lsl #32 +.Ltail_maybe_long: + cmp count, #64 + b.ge .Lnot_short +.Ltail_maybe_tiny: + cmp count, #15 + b.le .Ltail15tiny +.Ltail63: + ands tmp1, count, #0x30 + b.eq .Ltail15 + add dst, dst, tmp1 + cmp tmp1w, #0x20 + b.eq 1f + b.lt 2f + stp A_l, A_l, [dst, #-48] +1: + stp A_l, A_l, [dst, #-32] +2: + stp A_l, A_l, [dst, #-16] + +.Ltail15: + and count, count, #15 + add dst, dst, count + stp A_l, A_l, [dst, #-16] /* Repeat some/all of last store. */ + ret + +.Ltail15tiny: + /* Set up to 15 bytes. Does not assume earlier memory + being set. */ + tbz count, #3, 1f + str A_l, [dst], #8 +1: + tbz count, #2, 1f + str A_lw, [dst], #4 +1: + tbz count, #1, 1f + strh A_lw, [dst], #2 +1: + ret + + /* Critical loop. Start at a new cache line boundary. Assuming + * 64 bytes per line, this ensures the entire loop is in one line. */ + .p2align 6 +.Lnot_short: + neg tmp2, dst + ands tmp2, tmp2, #15 + b.eq 2f + /* Bring DST to 128-bit (16-byte) alignment. We know that there's + * more than that to set, so we simply store 16 bytes and advance by + * the amount required to reach alignment. */ + sub count, count, tmp2 + stp A_l, A_l, [dst] + add dst, dst, tmp2 + /* There may be less than 63 bytes to go now. */ + cmp count, #63 + b.le .Ltail63 +2: + sub dst, dst, #16 /* Pre-bias. */ + sub count, count, #64 +1: + stp A_l, A_l, [dst, #16] + stp A_l, A_l, [dst, #32] + stp A_l, A_l, [dst, #48] + stp A_l, A_l, [dst, #64]! + subs count, count, #64 + b.ge 1b + tst count, #0x3f + add dst, dst, #16 + b.ne .Ltail63 + ret + + /* For zeroing memory, check to see if we can use the ZVA feature to + * zero entire 'cache' lines. */ +.Lzero_mem: + mov A_l, #0 + cmp count, #63 + b.le .Ltail_maybe_tiny + neg tmp2, dst + ands tmp2, tmp2, #15 + b.eq 1f + sub count, count, tmp2 + stp A_l, A_l, [dst] + add dst, dst, tmp2 + cmp count, #63 + b.le .Ltail63 +1: + /* For zeroing small amounts of memory, it's not worth setting up + * the line-clear code. */ + cmp count, #128 + b.lt .Lnot_short + mrs tmp1, dczid_el0 + tbnz tmp1, #4, .Lnot_short + mov tmp3w, #4 + and zva_len, tmp1w, #15 /* Safety: other bits reserved. */ + lsl zva_len, tmp3w, zva_len + +.Lzero_by_line: + /* Compute how far we need to go to become suitably aligned. We're + * already at quad-word alignment. */ + cmp count, zva_len_x + b.lt .Lnot_short /* Not enough to reach alignment. */ + sub zva_bits_x, zva_len_x, #1 + neg tmp2, dst + ands tmp2, tmp2, zva_bits_x + b.eq 1f /* Already aligned. */ + /* Not aligned, check that there's enough to copy after alignment. */ + sub tmp1, count, tmp2 + cmp tmp1, #64 + ccmp tmp1, zva_len_x, #8, ge /* NZCV=0b1000 */ + b.lt .Lnot_short + /* We know that there's at least 64 bytes to zero and that it's safe + * to overrun by 64 bytes. */ + mov count, tmp1 +2: + stp A_l, A_l, [dst] + stp A_l, A_l, [dst, #16] + stp A_l, A_l, [dst, #32] + subs tmp2, tmp2, #64 + stp A_l, A_l, [dst, #48] + add dst, dst, #64 + b.ge 2b + /* We've overrun a bit, so adjust dst downwards. */ + add dst, dst, tmp2 +1: + sub count, count, zva_len_x +3: + dc zva, dst + add dst, dst, zva_len_x + subs count, count, zva_len_x + b.ge 3b + ands count, count, zva_bits_x + b.ne .Ltail_maybe_long + ret +END(android_memset32) diff --git a/libcutils/arch-x86/cache_wrapper.S b/libcutils/arch-x86/cache_wrapper.S index 508fdd3..9eee25c 100644 --- a/libcutils/arch-x86/cache_wrapper.S +++ b/libcutils/arch-x86/cache_wrapper.S @@ -17,8 +17,15 @@ * Contributed by: Intel Corporation */ +#if defined(__slm__) +/* Values are optimized for Silvermont */ +#define SHARED_CACHE_SIZE (1024*1024) /* Silvermont L2 Cache */ +#define DATA_CACHE_SIZE (24*1024) /* Silvermont L1 Data Cache */ +#else /* Values are optimized for Atom */ -#define SHARED_CACHE_SIZE (512*1024) /* Atom L2 Cache */ -#define DATA_CACHE_SIZE (24*1024) /* Atom L1 Data Cache */ +#define SHARED_CACHE_SIZE (512*1024) /* Atom L2 Cache */ +#define DATA_CACHE_SIZE (24*1024) /* Atom L1 Data Cache */ +#endif + #define SHARED_CACHE_SIZE_HALF (SHARED_CACHE_SIZE / 2) #define DATA_CACHE_SIZE_HALF (DATA_CACHE_SIZE / 2) diff --git a/libcutils/arch-x86_64/android_memset16_SSE2-atom.S b/libcutils/arch-x86_64/android_memset16_SSE2-atom.S new file mode 100644 index 0000000..48a10ed --- /dev/null +++ b/libcutils/arch-x86_64/android_memset16_SSE2-atom.S @@ -0,0 +1,564 @@ +/* + * Copyright (C) 2014 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. + */ +/* + * Contributed by: Intel Corporation + */ + +#include "cache.h" + +#ifndef L +# define L(label) .L##label +#endif + +#ifndef ALIGN +# define ALIGN(n) .p2align n +#endif + +#ifndef cfi_startproc +# define cfi_startproc .cfi_startproc +#endif + +#ifndef cfi_endproc +# define cfi_endproc .cfi_endproc +#endif + +#ifndef ENTRY +# define ENTRY(name) \ + .type name, @function; \ + .globl name; \ + .p2align 4; \ +name: \ + cfi_startproc +#endif + +#ifndef END +# define END(name) \ + cfi_endproc; \ + .size name, .-name +#endif + +#define JMPTBL(I, B) I - B + +/* Branch to an entry in a jump table. TABLE is a jump table with + relative offsets. INDEX is a register contains the index into the + jump table. SCALE is the scale of INDEX. */ +#define BRANCH_TO_JMPTBL_ENTRY(TABLE, INDEX, SCALE) \ + lea TABLE(%rip), %r11; \ + movslq (%r11, INDEX, SCALE), INDEX; \ + lea (%r11, INDEX), INDEX; \ + jmp *INDEX + + .section .text.sse2,"ax",@progbits + ALIGN (4) +ENTRY (android_memset16) // Address in rdi + shr $1, %rdx // Count in rdx + movzwl %si, %ecx + /* Fill the whole ECX with pattern. */ + shl $16, %esi + or %esi, %ecx // Pattern in ecx + + cmp $32, %rdx + jae L(32wordsormore) + +L(write_less32words): + lea (%rdi, %rdx, 2), %rdi + BRANCH_TO_JMPTBL_ENTRY (L(table_less32words), %rdx, 4) + + .pushsection .rodata.sse2,"a",@progbits + ALIGN (2) +L(table_less32words): + .int JMPTBL (L(write_0words), L(table_less32words)) + .int JMPTBL (L(write_1words), L(table_less32words)) + .int JMPTBL (L(write_2words), L(table_less32words)) + .int JMPTBL (L(write_3words), L(table_less32words)) + .int JMPTBL (L(write_4words), L(table_less32words)) + .int JMPTBL (L(write_5words), L(table_less32words)) + .int JMPTBL (L(write_6words), L(table_less32words)) + .int JMPTBL (L(write_7words), L(table_less32words)) + .int JMPTBL (L(write_8words), L(table_less32words)) + .int JMPTBL (L(write_9words), L(table_less32words)) + .int JMPTBL (L(write_10words), L(table_less32words)) + .int JMPTBL (L(write_11words), L(table_less32words)) + .int JMPTBL (L(write_12words), L(table_less32words)) + .int JMPTBL (L(write_13words), L(table_less32words)) + .int JMPTBL (L(write_14words), L(table_less32words)) + .int JMPTBL (L(write_15words), L(table_less32words)) + .int JMPTBL (L(write_16words), L(table_less32words)) + .int JMPTBL (L(write_17words), L(table_less32words)) + .int JMPTBL (L(write_18words), L(table_less32words)) + .int JMPTBL (L(write_19words), L(table_less32words)) + .int JMPTBL (L(write_20words), L(table_less32words)) + .int JMPTBL (L(write_21words), L(table_less32words)) + .int JMPTBL (L(write_22words), L(table_less32words)) + .int JMPTBL (L(write_23words), L(table_less32words)) + .int JMPTBL (L(write_24words), L(table_less32words)) + .int JMPTBL (L(write_25words), L(table_less32words)) + .int JMPTBL (L(write_26words), L(table_less32words)) + .int JMPTBL (L(write_27words), L(table_less32words)) + .int JMPTBL (L(write_28words), L(table_less32words)) + .int JMPTBL (L(write_29words), L(table_less32words)) + .int JMPTBL (L(write_30words), L(table_less32words)) + .int JMPTBL (L(write_31words), L(table_less32words)) + .popsection + + ALIGN (4) +L(write_28words): + movl %ecx, -56(%rdi) + movl %ecx, -52(%rdi) +L(write_24words): + movl %ecx, -48(%rdi) + movl %ecx, -44(%rdi) +L(write_20words): + movl %ecx, -40(%rdi) + movl %ecx, -36(%rdi) +L(write_16words): + movl %ecx, -32(%rdi) + movl %ecx, -28(%rdi) +L(write_12words): + movl %ecx, -24(%rdi) + movl %ecx, -20(%rdi) +L(write_8words): + movl %ecx, -16(%rdi) + movl %ecx, -12(%rdi) +L(write_4words): + movl %ecx, -8(%rdi) + movl %ecx, -4(%rdi) +L(write_0words): + ret + + ALIGN (4) +L(write_29words): + movl %ecx, -58(%rdi) + movl %ecx, -54(%rdi) +L(write_25words): + movl %ecx, -50(%rdi) + movl %ecx, -46(%rdi) +L(write_21words): + movl %ecx, -42(%rdi) + movl %ecx, -38(%rdi) +L(write_17words): + movl %ecx, -34(%rdi) + movl %ecx, -30(%rdi) +L(write_13words): + movl %ecx, -26(%rdi) + movl %ecx, -22(%rdi) +L(write_9words): + movl %ecx, -18(%rdi) + movl %ecx, -14(%rdi) +L(write_5words): + movl %ecx, -10(%rdi) + movl %ecx, -6(%rdi) +L(write_1words): + mov %cx, -2(%rdi) + ret + + ALIGN (4) +L(write_30words): + movl %ecx, -60(%rdi) + movl %ecx, -56(%rdi) +L(write_26words): + movl %ecx, -52(%rdi) + movl %ecx, -48(%rdi) +L(write_22words): + movl %ecx, -44(%rdi) + movl %ecx, -40(%rdi) +L(write_18words): + movl %ecx, -36(%rdi) + movl %ecx, -32(%rdi) +L(write_14words): + movl %ecx, -28(%rdi) + movl %ecx, -24(%rdi) +L(write_10words): + movl %ecx, -20(%rdi) + movl %ecx, -16(%rdi) +L(write_6words): + movl %ecx, -12(%rdi) + movl %ecx, -8(%rdi) +L(write_2words): + movl %ecx, -4(%rdi) + ret + + ALIGN (4) +L(write_31words): + movl %ecx, -62(%rdi) + movl %ecx, -58(%rdi) +L(write_27words): + movl %ecx, -54(%rdi) + movl %ecx, -50(%rdi) +L(write_23words): + movl %ecx, -46(%rdi) + movl %ecx, -42(%rdi) +L(write_19words): + movl %ecx, -38(%rdi) + movl %ecx, -34(%rdi) +L(write_15words): + movl %ecx, -30(%rdi) + movl %ecx, -26(%rdi) +L(write_11words): + movl %ecx, -22(%rdi) + movl %ecx, -18(%rdi) +L(write_7words): + movl %ecx, -14(%rdi) + movl %ecx, -10(%rdi) +L(write_3words): + movl %ecx, -6(%rdi) + movw %cx, -2(%rdi) + ret + + ALIGN (4) +L(32wordsormore): + shl $1, %rdx + test $0x01, %edi + jz L(aligned2bytes) + mov %ecx, (%rdi) + mov %ecx, -4(%rdi, %rdx) + sub $2, %rdx + add $1, %rdi + rol $8, %ecx +L(aligned2bytes): + /* Fill xmm0 with the pattern. */ + movd %ecx, %xmm0 + pshufd $0, %xmm0, %xmm0 + + testl $0xf, %edi + jz L(aligned_16) +/* RDX > 32 and RDI is not 16 byte aligned. */ + movdqu %xmm0, (%rdi) + mov %rdi, %rsi + and $-16, %rdi + add $16, %rdi + sub %rdi, %rsi + add %rsi, %rdx + + ALIGN (4) +L(aligned_16): + cmp $128, %rdx + jge L(128bytesormore) + +L(aligned_16_less128bytes): + add %rdx, %rdi + shr $1, %rdx + BRANCH_TO_JMPTBL_ENTRY (L(table_16_128bytes), %rdx, 4) + + ALIGN (4) +L(128bytesormore): + cmp $SHARED_CACHE_SIZE, %rdx + jg L(128bytesormore_nt) + +L(128bytesormore_normal): + sub $128, %rdx + movdqa %xmm0, (%rdi) + movdqa %xmm0, 0x10(%rdi) + movdqa %xmm0, 0x20(%rdi) + movdqa %xmm0, 0x30(%rdi) + movdqa %xmm0, 0x40(%rdi) + movdqa %xmm0, 0x50(%rdi) + movdqa %xmm0, 0x60(%rdi) + movdqa %xmm0, 0x70(%rdi) + lea 128(%rdi), %rdi + cmp $128, %rdx + jl L(128bytesless_normal) + + sub $128, %rdx + movdqa %xmm0, (%rdi) + movdqa %xmm0, 0x10(%rdi) + movdqa %xmm0, 0x20(%rdi) + movdqa %xmm0, 0x30(%rdi) + movdqa %xmm0, 0x40(%rdi) + movdqa %xmm0, 0x50(%rdi) + movdqa %xmm0, 0x60(%rdi) + movdqa %xmm0, 0x70(%rdi) + lea 128(%rdi), %rdi + cmp $128, %rdx + jl L(128bytesless_normal) + + sub $128, %rdx + movdqa %xmm0, (%rdi) + movdqa %xmm0, 0x10(%rdi) + movdqa %xmm0, 0x20(%rdi) + movdqa %xmm0, 0x30(%rdi) + movdqa %xmm0, 0x40(%rdi) + movdqa %xmm0, 0x50(%rdi) + movdqa %xmm0, 0x60(%rdi) + movdqa %xmm0, 0x70(%rdi) + lea 128(%rdi), %rdi + cmp $128, %rdx + jl L(128bytesless_normal) + + sub $128, %rdx + movdqa %xmm0, (%rdi) + movdqa %xmm0, 0x10(%rdi) + movdqa %xmm0, 0x20(%rdi) + movdqa %xmm0, 0x30(%rdi) + movdqa %xmm0, 0x40(%rdi) + movdqa %xmm0, 0x50(%rdi) + movdqa %xmm0, 0x60(%rdi) + movdqa %xmm0, 0x70(%rdi) + lea 128(%rdi), %rdi + cmp $128, %rdx + jge L(128bytesormore_normal) + +L(128bytesless_normal): + add %rdx, %rdi + shr $1, %rdx + BRANCH_TO_JMPTBL_ENTRY (L(table_16_128bytes), %rdx, 4) + + ALIGN (4) +L(128bytesormore_nt): + sub $128, %rdx + movntdq %xmm0, (%rdi) + movntdq %xmm0, 0x10(%rdi) + movntdq %xmm0, 0x20(%rdi) + movntdq %xmm0, 0x30(%rdi) + movntdq %xmm0, 0x40(%rdi) + movntdq %xmm0, 0x50(%rdi) + movntdq %xmm0, 0x60(%rdi) + movntdq %xmm0, 0x70(%rdi) + lea 128(%rdi), %rdi + cmp $128, %rdx + jge L(128bytesormore_nt) + + sfence + add %rdx, %rdi + shr $1, %rdx + BRANCH_TO_JMPTBL_ENTRY (L(table_16_128bytes), %rdx, 4) + + .pushsection .rodata.sse2,"a",@progbits + ALIGN (2) +L(table_16_128bytes): + .int JMPTBL (L(aligned_16_0bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_2bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_4bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_6bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_8bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_10bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_12bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_14bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_16bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_18bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_20bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_22bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_24bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_26bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_28bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_30bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_32bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_34bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_36bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_38bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_40bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_42bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_44bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_46bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_48bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_50bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_52bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_54bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_56bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_58bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_60bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_62bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_64bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_66bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_68bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_70bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_72bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_74bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_76bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_78bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_80bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_82bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_84bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_86bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_88bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_90bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_92bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_94bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_96bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_98bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_100bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_102bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_104bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_106bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_108bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_110bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_112bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_114bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_116bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_118bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_120bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_122bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_124bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_126bytes), L(table_16_128bytes)) + .popsection + + ALIGN (4) +L(aligned_16_112bytes): + movdqa %xmm0, -112(%rdi) +L(aligned_16_96bytes): + movdqa %xmm0, -96(%rdi) +L(aligned_16_80bytes): + movdqa %xmm0, -80(%rdi) +L(aligned_16_64bytes): + movdqa %xmm0, -64(%rdi) +L(aligned_16_48bytes): + movdqa %xmm0, -48(%rdi) +L(aligned_16_32bytes): + movdqa %xmm0, -32(%rdi) +L(aligned_16_16bytes): + movdqa %xmm0, -16(%rdi) +L(aligned_16_0bytes): + ret + + ALIGN (4) +L(aligned_16_114bytes): + movdqa %xmm0, -114(%rdi) +L(aligned_16_98bytes): + movdqa %xmm0, -98(%rdi) +L(aligned_16_82bytes): + movdqa %xmm0, -82(%rdi) +L(aligned_16_66bytes): + movdqa %xmm0, -66(%rdi) +L(aligned_16_50bytes): + movdqa %xmm0, -50(%rdi) +L(aligned_16_34bytes): + movdqa %xmm0, -34(%rdi) +L(aligned_16_18bytes): + movdqa %xmm0, -18(%rdi) +L(aligned_16_2bytes): + movw %cx, -2(%rdi) + ret + + ALIGN (4) +L(aligned_16_116bytes): + movdqa %xmm0, -116(%rdi) +L(aligned_16_100bytes): + movdqa %xmm0, -100(%rdi) +L(aligned_16_84bytes): + movdqa %xmm0, -84(%rdi) +L(aligned_16_68bytes): + movdqa %xmm0, -68(%rdi) +L(aligned_16_52bytes): + movdqa %xmm0, -52(%rdi) +L(aligned_16_36bytes): + movdqa %xmm0, -36(%rdi) +L(aligned_16_20bytes): + movdqa %xmm0, -20(%rdi) +L(aligned_16_4bytes): + movl %ecx, -4(%rdi) + ret + + ALIGN (4) +L(aligned_16_118bytes): + movdqa %xmm0, -118(%rdi) +L(aligned_16_102bytes): + movdqa %xmm0, -102(%rdi) +L(aligned_16_86bytes): + movdqa %xmm0, -86(%rdi) +L(aligned_16_70bytes): + movdqa %xmm0, -70(%rdi) +L(aligned_16_54bytes): + movdqa %xmm0, -54(%rdi) +L(aligned_16_38bytes): + movdqa %xmm0, -38(%rdi) +L(aligned_16_22bytes): + movdqa %xmm0, -22(%rdi) +L(aligned_16_6bytes): + movl %ecx, -6(%rdi) + movw %cx, -2(%rdi) + ret + + ALIGN (4) +L(aligned_16_120bytes): + movdqa %xmm0, -120(%rdi) +L(aligned_16_104bytes): + movdqa %xmm0, -104(%rdi) +L(aligned_16_88bytes): + movdqa %xmm0, -88(%rdi) +L(aligned_16_72bytes): + movdqa %xmm0, -72(%rdi) +L(aligned_16_56bytes): + movdqa %xmm0, -56(%rdi) +L(aligned_16_40bytes): + movdqa %xmm0, -40(%rdi) +L(aligned_16_24bytes): + movdqa %xmm0, -24(%rdi) +L(aligned_16_8bytes): + movq %xmm0, -8(%rdi) + ret + + ALIGN (4) +L(aligned_16_122bytes): + movdqa %xmm0, -122(%rdi) +L(aligned_16_106bytes): + movdqa %xmm0, -106(%rdi) +L(aligned_16_90bytes): + movdqa %xmm0, -90(%rdi) +L(aligned_16_74bytes): + movdqa %xmm0, -74(%rdi) +L(aligned_16_58bytes): + movdqa %xmm0, -58(%rdi) +L(aligned_16_42bytes): + movdqa %xmm0, -42(%rdi) +L(aligned_16_26bytes): + movdqa %xmm0, -26(%rdi) +L(aligned_16_10bytes): + movq %xmm0, -10(%rdi) + movw %cx, -2(%rdi) + ret + + ALIGN (4) +L(aligned_16_124bytes): + movdqa %xmm0, -124(%rdi) +L(aligned_16_108bytes): + movdqa %xmm0, -108(%rdi) +L(aligned_16_92bytes): + movdqa %xmm0, -92(%rdi) +L(aligned_16_76bytes): + movdqa %xmm0, -76(%rdi) +L(aligned_16_60bytes): + movdqa %xmm0, -60(%rdi) +L(aligned_16_44bytes): + movdqa %xmm0, -44(%rdi) +L(aligned_16_28bytes): + movdqa %xmm0, -28(%rdi) +L(aligned_16_12bytes): + movq %xmm0, -12(%rdi) + movl %ecx, -4(%rdi) + ret + + ALIGN (4) +L(aligned_16_126bytes): + movdqa %xmm0, -126(%rdi) +L(aligned_16_110bytes): + movdqa %xmm0, -110(%rdi) +L(aligned_16_94bytes): + movdqa %xmm0, -94(%rdi) +L(aligned_16_78bytes): + movdqa %xmm0, -78(%rdi) +L(aligned_16_62bytes): + movdqa %xmm0, -62(%rdi) +L(aligned_16_46bytes): + movdqa %xmm0, -46(%rdi) +L(aligned_16_30bytes): + movdqa %xmm0, -30(%rdi) +L(aligned_16_14bytes): + movq %xmm0, -14(%rdi) + movl %ecx, -6(%rdi) + movw %cx, -2(%rdi) + ret + +END (android_memset16) diff --git a/libcutils/arch-x86_64/android_memset32_SSE2-atom.S b/libcutils/arch-x86_64/android_memset32_SSE2-atom.S new file mode 100644 index 0000000..4bdea8e --- /dev/null +++ b/libcutils/arch-x86_64/android_memset32_SSE2-atom.S @@ -0,0 +1,372 @@ +/* + * Copyright (C) 2014 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. + */ +/* + * Contributed by: Intel Corporation + */ + +#include "cache.h" + +#ifndef L +# define L(label) .L##label +#endif + +#ifndef ALIGN +# define ALIGN(n) .p2align n +#endif + +#ifndef cfi_startproc +# define cfi_startproc .cfi_startproc +#endif + +#ifndef cfi_endproc +# define cfi_endproc .cfi_endproc +#endif + +#ifndef ENTRY +# define ENTRY(name) \ + .type name, @function; \ + .globl name; \ + .p2align 4; \ +name: \ + cfi_startproc +#endif + +#ifndef END +# define END(name) \ + cfi_endproc; \ + .size name, .-name +#endif + +#define JMPTBL(I, B) I - B + +/* Branch to an entry in a jump table. TABLE is a jump table with + relative offsets. INDEX is a register contains the index into the + jump table. SCALE is the scale of INDEX. */ +#define BRANCH_TO_JMPTBL_ENTRY(TABLE, INDEX, SCALE) \ + lea TABLE(%rip), %r11; \ + movslq (%r11, INDEX, SCALE), INDEX; \ + lea (%r11, INDEX), INDEX; \ + jmp *INDEX + + .section .text.sse2,"ax",@progbits + ALIGN (4) +ENTRY (android_memset32) // Address in rdi + shr $2, %rdx // Count in rdx + movl %esi, %ecx // Pattern in ecx + + cmp $16, %rdx + jae L(16dbwordsormore) + +L(write_less16dbwords): + lea (%rdi, %rdx, 4), %rdi + BRANCH_TO_JMPTBL_ENTRY (L(table_less16dbwords), %rdx, 4) + + .pushsection .rodata.sse2,"a",@progbits + ALIGN (2) +L(table_less16dbwords): + .int JMPTBL (L(write_0dbwords), L(table_less16dbwords)) + .int JMPTBL (L(write_1dbwords), L(table_less16dbwords)) + .int JMPTBL (L(write_2dbwords), L(table_less16dbwords)) + .int JMPTBL (L(write_3dbwords), L(table_less16dbwords)) + .int JMPTBL (L(write_4dbwords), L(table_less16dbwords)) + .int JMPTBL (L(write_5dbwords), L(table_less16dbwords)) + .int JMPTBL (L(write_6dbwords), L(table_less16dbwords)) + .int JMPTBL (L(write_7dbwords), L(table_less16dbwords)) + .int JMPTBL (L(write_8dbwords), L(table_less16dbwords)) + .int JMPTBL (L(write_9dbwords), L(table_less16dbwords)) + .int JMPTBL (L(write_10dbwords), L(table_less16dbwords)) + .int JMPTBL (L(write_11dbwords), L(table_less16dbwords)) + .int JMPTBL (L(write_12dbwords), L(table_less16dbwords)) + .int JMPTBL (L(write_13dbwords), L(table_less16dbwords)) + .int JMPTBL (L(write_14dbwords), L(table_less16dbwords)) + .int JMPTBL (L(write_15dbwords), L(table_less16dbwords)) + .popsection + + ALIGN (4) +L(write_15dbwords): + movl %ecx, -60(%rdi) +L(write_14dbwords): + movl %ecx, -56(%rdi) +L(write_13dbwords): + movl %ecx, -52(%rdi) +L(write_12dbwords): + movl %ecx, -48(%rdi) +L(write_11dbwords): + movl %ecx, -44(%rdi) +L(write_10dbwords): + movl %ecx, -40(%rdi) +L(write_9dbwords): + movl %ecx, -36(%rdi) +L(write_8dbwords): + movl %ecx, -32(%rdi) +L(write_7dbwords): + movl %ecx, -28(%rdi) +L(write_6dbwords): + movl %ecx, -24(%rdi) +L(write_5dbwords): + movl %ecx, -20(%rdi) +L(write_4dbwords): + movl %ecx, -16(%rdi) +L(write_3dbwords): + movl %ecx, -12(%rdi) +L(write_2dbwords): + movl %ecx, -8(%rdi) +L(write_1dbwords): + movl %ecx, -4(%rdi) +L(write_0dbwords): + ret + + ALIGN (4) +L(16dbwordsormore): + test $3, %edi + jz L(aligned4bytes) + mov %ecx, (%rdi) + mov %ecx, -4(%rdi, %rdx, 4) + sub $1, %rdx + rol $24, %ecx + add $1, %rdi + test $3, %edi + jz L(aligned4bytes) + ror $8, %ecx + add $1, %rdi + test $3, %edi + jz L(aligned4bytes) + ror $8, %ecx + add $1, %rdi +L(aligned4bytes): + shl $2, %rdx + + /* Fill xmm0 with the pattern. */ + movd %ecx, %xmm0 + pshufd $0, %xmm0, %xmm0 + + testl $0xf, %edi + jz L(aligned_16) +/* RDX > 32 and RDI is not 16 byte aligned. */ + movdqu %xmm0, (%rdi) + mov %rdi, %rsi + and $-16, %rdi + add $16, %rdi + sub %rdi, %rsi + add %rsi, %rdx + + ALIGN (4) +L(aligned_16): + cmp $128, %rdx + jge L(128bytesormore) + +L(aligned_16_less128bytes): + add %rdx, %rdi + shr $2, %rdx + BRANCH_TO_JMPTBL_ENTRY (L(table_16_128bytes), %rdx, 4) + + ALIGN (4) +L(128bytesormore): + cmp $SHARED_CACHE_SIZE, %rdx + jg L(128bytesormore_nt) + +L(128bytesormore_normal): + sub $128, %rdx + movdqa %xmm0, (%rdi) + movdqa %xmm0, 0x10(%rdi) + movdqa %xmm0, 0x20(%rdi) + movdqa %xmm0, 0x30(%rdi) + movdqa %xmm0, 0x40(%rdi) + movdqa %xmm0, 0x50(%rdi) + movdqa %xmm0, 0x60(%rdi) + movdqa %xmm0, 0x70(%rdi) + lea 128(%rdi), %rdi + cmp $128, %rdx + jl L(128bytesless_normal) + + sub $128, %rdx + movdqa %xmm0, (%rdi) + movdqa %xmm0, 0x10(%rdi) + movdqa %xmm0, 0x20(%rdi) + movdqa %xmm0, 0x30(%rdi) + movdqa %xmm0, 0x40(%rdi) + movdqa %xmm0, 0x50(%rdi) + movdqa %xmm0, 0x60(%rdi) + movdqa %xmm0, 0x70(%rdi) + lea 128(%rdi), %rdi + cmp $128, %rdx + jl L(128bytesless_normal) + + sub $128, %rdx + movdqa %xmm0, (%rdi) + movdqa %xmm0, 0x10(%rdi) + movdqa %xmm0, 0x20(%rdi) + movdqa %xmm0, 0x30(%rdi) + movdqa %xmm0, 0x40(%rdi) + movdqa %xmm0, 0x50(%rdi) + movdqa %xmm0, 0x60(%rdi) + movdqa %xmm0, 0x70(%rdi) + lea 128(%rdi), %rdi + cmp $128, %rdx + jl L(128bytesless_normal) + + sub $128, %rdx + movdqa %xmm0, (%rdi) + movdqa %xmm0, 0x10(%rdi) + movdqa %xmm0, 0x20(%rdi) + movdqa %xmm0, 0x30(%rdi) + movdqa %xmm0, 0x40(%rdi) + movdqa %xmm0, 0x50(%rdi) + movdqa %xmm0, 0x60(%rdi) + movdqa %xmm0, 0x70(%rdi) + lea 128(%rdi), %rdi + cmp $128, %rdx + jge L(128bytesormore_normal) + +L(128bytesless_normal): + add %rdx, %rdi + shr $2, %rdx + BRANCH_TO_JMPTBL_ENTRY (L(table_16_128bytes), %rdx, 4) + + ALIGN (4) +L(128bytesormore_nt): + sub $128, %rdx + movntdq %xmm0, (%rdi) + movntdq %xmm0, 0x10(%rdi) + movntdq %xmm0, 0x20(%rdi) + movntdq %xmm0, 0x30(%rdi) + movntdq %xmm0, 0x40(%rdi) + movntdq %xmm0, 0x50(%rdi) + movntdq %xmm0, 0x60(%rdi) + movntdq %xmm0, 0x70(%rdi) + lea 128(%rdi), %rdi + cmp $128, %rdx + jge L(128bytesormore_nt) + + sfence + add %rdx, %rdi + shr $2, %rdx + BRANCH_TO_JMPTBL_ENTRY (L(table_16_128bytes), %rdx, 4) + + .pushsection .rodata.sse2,"a",@progbits + ALIGN (2) +L(table_16_128bytes): + .int JMPTBL (L(aligned_16_0bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_4bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_8bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_12bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_16bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_20bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_24bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_28bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_32bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_36bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_40bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_44bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_48bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_52bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_56bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_60bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_64bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_68bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_72bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_76bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_80bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_84bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_88bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_92bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_96bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_100bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_104bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_108bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_112bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_116bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_120bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_124bytes), L(table_16_128bytes)) + .popsection + + ALIGN (4) +L(aligned_16_112bytes): + movdqa %xmm0, -112(%rdi) +L(aligned_16_96bytes): + movdqa %xmm0, -96(%rdi) +L(aligned_16_80bytes): + movdqa %xmm0, -80(%rdi) +L(aligned_16_64bytes): + movdqa %xmm0, -64(%rdi) +L(aligned_16_48bytes): + movdqa %xmm0, -48(%rdi) +L(aligned_16_32bytes): + movdqa %xmm0, -32(%rdi) +L(aligned_16_16bytes): + movdqa %xmm0, -16(%rdi) +L(aligned_16_0bytes): + ret + + ALIGN (4) +L(aligned_16_116bytes): + movdqa %xmm0, -116(%rdi) +L(aligned_16_100bytes): + movdqa %xmm0, -100(%rdi) +L(aligned_16_84bytes): + movdqa %xmm0, -84(%rdi) +L(aligned_16_68bytes): + movdqa %xmm0, -68(%rdi) +L(aligned_16_52bytes): + movdqa %xmm0, -52(%rdi) +L(aligned_16_36bytes): + movdqa %xmm0, -36(%rdi) +L(aligned_16_20bytes): + movdqa %xmm0, -20(%rdi) +L(aligned_16_4bytes): + movl %ecx, -4(%rdi) + ret + + ALIGN (4) +L(aligned_16_120bytes): + movdqa %xmm0, -120(%rdi) +L(aligned_16_104bytes): + movdqa %xmm0, -104(%rdi) +L(aligned_16_88bytes): + movdqa %xmm0, -88(%rdi) +L(aligned_16_72bytes): + movdqa %xmm0, -72(%rdi) +L(aligned_16_56bytes): + movdqa %xmm0, -56(%rdi) +L(aligned_16_40bytes): + movdqa %xmm0, -40(%rdi) +L(aligned_16_24bytes): + movdqa %xmm0, -24(%rdi) +L(aligned_16_8bytes): + movq %xmm0, -8(%rdi) + ret + + ALIGN (4) +L(aligned_16_124bytes): + movdqa %xmm0, -124(%rdi) +L(aligned_16_108bytes): + movdqa %xmm0, -108(%rdi) +L(aligned_16_92bytes): + movdqa %xmm0, -92(%rdi) +L(aligned_16_76bytes): + movdqa %xmm0, -76(%rdi) +L(aligned_16_60bytes): + movdqa %xmm0, -60(%rdi) +L(aligned_16_44bytes): + movdqa %xmm0, -44(%rdi) +L(aligned_16_28bytes): + movdqa %xmm0, -28(%rdi) +L(aligned_16_12bytes): + movq %xmm0, -12(%rdi) + movl %ecx, -4(%rdi) + ret + +END (android_memset32) diff --git a/include/corkscrew/demangle.h b/libcutils/arch-x86_64/cache.h index 04b0225..ab5dd2f 100644 --- a/include/corkscrew/demangle.h +++ b/libcutils/arch-x86_64/cache.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 The Android Open Source Project + * Copyright (C) 2014 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. @@ -13,30 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -/* C++ symbol name demangling. */ - -#ifndef _CORKSCREW_DEMANGLE_H -#define _CORKSCREW_DEMANGLE_H - -#include <sys/types.h> -#include <stdbool.h> - -#ifdef __cplusplus -extern "C" { -#endif - /* - * Demangles a C++ symbol name. - * If name is NULL or if the name cannot be demangled, returns NULL. - * Otherwise, returns a newly allocated string that contains the demangled name. - * - * The caller must free the returned string using free(). + * Contributed by: Intel Corporation */ -char* demangle_symbol_name(const char* name); -#ifdef __cplusplus -} +#if defined(__slm__) +/* Values are optimized for Silvermont */ +#define SHARED_CACHE_SIZE (1024*1024) /* Silvermont L2 Cache */ +#define DATA_CACHE_SIZE (24*1024) /* Silvermont L1 Data Cache */ +#else +/* Values are optimized for Atom */ +#define SHARED_CACHE_SIZE (512*1024) /* Atom L2 Cache */ +#define DATA_CACHE_SIZE (24*1024) /* Atom L1 Data Cache */ #endif -#endif // _CORKSCREW_DEMANGLE_H +#define SHARED_CACHE_SIZE_HALF (SHARED_CACHE_SIZE / 2) +#define DATA_CACHE_SIZE_HALF (DATA_CACHE_SIZE / 2) diff --git a/libcutils/ashmem-host.c b/libcutils/ashmem-host.c index f03e130..4ac4f57 100644 --- a/libcutils/ashmem-host.c +++ b/libcutils/ashmem-host.c @@ -19,96 +19,102 @@ * an ashmem-enabled kernel. See ashmem-dev.c for the real ashmem-based version. */ -#include <unistd.h> -#include <string.h> -#include <stdlib.h> -#include <sys/types.h> -#include <sys/stat.h> +#include <errno.h> #include <fcntl.h> +#include <limits.h> +#include <pthread.h> +#include <stdbool.h> #include <stdio.h> -#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> #include <time.h> -#include <limits.h> +#include <unistd.h> #include <cutils/ashmem.h> -int ashmem_create_region(const char *ignored, size_t size) -{ - static const char txt[] = "abcdefghijklmnopqrstuvwxyz" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - char name[64]; - unsigned int retries = 0; - pid_t pid = getpid(); - int fd; - - srand(time(NULL) + pid); - -retry: - /* not beautiful, its just wolf-like loop unrolling */ - snprintf(name, sizeof(name), "/tmp/android-ashmem-%d-%c%c%c%c%c%c%c%c", - pid, - txt[(int) ((sizeof(txt) - 1) * (rand() / (RAND_MAX + 1.0)))], - txt[(int) ((sizeof(txt) - 1) * (rand() / (RAND_MAX + 1.0)))], - txt[(int) ((sizeof(txt) - 1) * (rand() / (RAND_MAX + 1.0)))], - txt[(int) ((sizeof(txt) - 1) * (rand() / (RAND_MAX + 1.0)))], - txt[(int) ((sizeof(txt) - 1) * (rand() / (RAND_MAX + 1.0)))], - txt[(int) ((sizeof(txt) - 1) * (rand() / (RAND_MAX + 1.0)))], - txt[(int) ((sizeof(txt) - 1) * (rand() / (RAND_MAX + 1.0)))], - txt[(int) ((sizeof(txt) - 1) * (rand() / (RAND_MAX + 1.0)))]); - - /* open O_EXCL & O_CREAT: we are either the sole owner or we fail */ - fd = open(name, O_RDWR | O_CREAT | O_EXCL, 0600); - if (fd == -1) { - /* unlikely, but if we failed because `name' exists, retry */ - if (errno == EEXIST && ++retries < 6) - goto retry; - return -1; - } - - /* truncate the file to `len' bytes */ - if (ftruncate(fd, size) == -1) - goto error; +#ifndef __unused +#define __unused __attribute__((__unused__)) +#endif - if (unlink(name) == -1) - goto error; +static pthread_once_t seed_initialized = PTHREAD_ONCE_INIT; +static void initialize_random() { + srand(time(NULL) + getpid()); +} - return fd; -error: - close(fd); - return -1; +int ashmem_create_region(const char *ignored __unused, size_t size) +{ + static const char txt[] = "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + char name[64]; + unsigned int retries = 0; + pid_t pid = getpid(); + int fd; + if (pthread_once(&seed_initialized, &initialize_random) != 0) { + return -1; + } + do { + /* not beautiful, its just wolf-like loop unrolling */ + snprintf(name, sizeof(name), "/tmp/android-ashmem-%d-%c%c%c%c%c%c%c%c", + pid, + txt[(int) ((sizeof(txt) - 1) * (rand() / (RAND_MAX + 1.0)))], + txt[(int) ((sizeof(txt) - 1) * (rand() / (RAND_MAX + 1.0)))], + txt[(int) ((sizeof(txt) - 1) * (rand() / (RAND_MAX + 1.0)))], + txt[(int) ((sizeof(txt) - 1) * (rand() / (RAND_MAX + 1.0)))], + txt[(int) ((sizeof(txt) - 1) * (rand() / (RAND_MAX + 1.0)))], + txt[(int) ((sizeof(txt) - 1) * (rand() / (RAND_MAX + 1.0)))], + txt[(int) ((sizeof(txt) - 1) * (rand() / (RAND_MAX + 1.0)))], + txt[(int) ((sizeof(txt) - 1) * (rand() / (RAND_MAX + 1.0)))]); + + /* open O_EXCL & O_CREAT: we are either the sole owner or we fail */ + fd = open(name, O_RDWR | O_CREAT | O_EXCL, 0600); + if (fd == -1) { + /* unlikely, but if we failed because `name' exists, retry */ + if (errno != EEXIST || ++retries >= 6) { + return -1; + } + } + } while (fd == -1); + /* truncate the file to `len' bytes */ + if (ftruncate(fd, size) != -1 && unlink(name) != -1) { + return fd; + } + close(fd); + return -1; } -int ashmem_set_prot_region(int fd, int prot) +int ashmem_set_prot_region(int fd __unused, int prot __unused) { - return 0; + return 0; } -int ashmem_pin_region(int fd, size_t offset, size_t len) +int ashmem_pin_region(int fd __unused, size_t offset __unused, size_t len __unused) { - return ASHMEM_NOT_PURGED; + return ASHMEM_NOT_PURGED; } -int ashmem_unpin_region(int fd, size_t offset, size_t len) +int ashmem_unpin_region(int fd __unused, size_t offset __unused, size_t len __unused) { - return ASHMEM_IS_UNPINNED; + return ASHMEM_IS_UNPINNED; } int ashmem_get_size_region(int fd) { - struct stat buf; - int result; - - result = fstat(fd, &buf); - if (result == -1) { - return -1; - } - - // Check if this is an "ashmem" region. - // TODO: This is very hacky, and can easily break. We need some reliable indicator. - if (!(buf.st_nlink == 0 && S_ISREG(buf.st_mode))) { - errno = ENOTTY; - return -1; - } - - return (int)buf.st_size; // TODO: care about overflow (> 2GB file)? + struct stat buf; + int result; + + result = fstat(fd, &buf); + if (result == -1) { + return -1; + } + + // Check if this is an "ashmem" region. + // TODO: This is very hacky, and can easily break. We need some reliable indicator. + if (!(buf.st_nlink == 0 && S_ISREG(buf.st_mode))) { + errno = ENOTTY; + return -1; + } + + return (int)buf.st_size; // TODO: care about overflow (> 2GB file)? } diff --git a/libcutils/cpu_info.c b/libcutils/cpu_info.c index 23dda8a..21fa1dc 100644 --- a/libcutils/cpu_info.c +++ b/libcutils/cpu_info.c @@ -1,5 +1,4 @@ -/* libs/cutils/cpu_info.c -** +/* ** Copyright 2007, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,11 +14,12 @@ ** limitations under the License. */ -#include <cutils/cpu_info.h> -#include <stdlib.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> +#include <cutils/cpu_info.h> + // we cache the serial number here. // this is also used as a fgets() line buffer when we are reading /proc/cpuinfo static char serial_number[100] = { 0 }; @@ -31,7 +31,6 @@ extern const char* get_cpu_serial_number(void) FILE* file; char* chp, *end; char* whitespace; - int length; // read serial number from /proc/cpuinfo file = fopen("proc/cpuinfo", "r"); diff --git a/libcutils/debugger.c b/libcutils/debugger.c index 7d907fc..056de5d 100644 --- a/libcutils/debugger.c +++ b/libcutils/debugger.c @@ -15,6 +15,7 @@ */ #include <stdlib.h> +#include <string.h> #include <unistd.h> #include <cutils/debugger.h> @@ -28,9 +29,9 @@ int dump_tombstone(pid_t tid, char* pathbuf, size_t pathlen) { } debugger_msg_t msg; + memset(&msg, 0, sizeof(msg)); msg.tid = tid; msg.action = DEBUGGER_ACTION_DUMP_TOMBSTONE; - msg.abort_msg_address = 0; int result = 0; if (TEMP_FAILURE_RETRY(write(s, &msg, sizeof(msg))) != sizeof(msg)) { @@ -62,9 +63,9 @@ int dump_backtrace_to_file(pid_t tid, int fd) { } debugger_msg_t msg; + memset(&msg, 0, sizeof(msg)); msg.tid = tid; msg.action = DEBUGGER_ACTION_DUMP_BACKTRACE; - msg.abort_msg_address = 0; int result = 0; if (TEMP_FAILURE_RETRY(write(s, &msg, sizeof(msg))) != sizeof(msg)) { diff --git a/libcutils/dlmalloc_stubs.c b/libcutils/dlmalloc_stubs.c index c327a55..2db473d 100644 --- a/libcutils/dlmalloc_stubs.c +++ b/libcutils/dlmalloc_stubs.c @@ -14,21 +14,22 @@ * limitations under the License. */ -#include "../../../bionic/libc/bionic/dlmalloc.h" -#include "cutils/log.h" +#include "log/log.h" + +#define UNUSED __attribute__((__unused__)) /* * Stubs for functions defined in bionic/libc/bionic/dlmalloc.c. These * are used in host builds, as the host libc will not contain these * functions. */ -void dlmalloc_inspect_all(void(*handler)(void*, void *, size_t, void*), - void* arg) +void dlmalloc_inspect_all(void(*handler)(void*, void *, size_t, void*) UNUSED, + void* arg UNUSED) { ALOGW("Called host unimplemented stub: dlmalloc_inspect_all"); } -int dlmalloc_trim(size_t unused) +int dlmalloc_trim(size_t unused UNUSED) { ALOGW("Called host unimplemented stub: dlmalloc_trim"); return 0; diff --git a/libcutils/iosched_policy.c b/libcutils/iosched_policy.c index 5d90a01..a6da9ca 100644 --- a/libcutils/iosched_policy.c +++ b/libcutils/iosched_policy.c @@ -1,5 +1,4 @@ /* -** ** Copyright 2007-2014, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,50 +14,41 @@ ** limitations under the License. */ +#include <errno.h> +#include <fcntl.h> #include <stdio.h> #include <stdlib.h> -#include <unistd.h> #include <string.h> -#include <errno.h> -#include <fcntl.h> - -#ifdef HAVE_SCHED_H +#include <unistd.h> #include <cutils/iosched_policy.h> #ifdef HAVE_ANDROID_OS -/* #include <linux/ioprio.h> */ -extern int ioprio_set(int which, int who, int ioprio); -extern int ioprio_get(int which, int who); +#include <linux/ioprio.h> +#include <sys/syscall.h> +#define __android_unused +#else +#define __android_unused __attribute__((__unused__)) #endif -enum { - WHO_PROCESS = 1, - WHO_PGRP, - WHO_USER, -}; - -#define CLASS_SHIFT 13 -#define IOPRIO_NORM 4 - -int android_set_ioprio(int pid, IoSchedClass clazz, int ioprio) { +int android_set_ioprio(int pid __android_unused, IoSchedClass clazz __android_unused, int ioprio __android_unused) { #ifdef HAVE_ANDROID_OS - if (ioprio_set(WHO_PROCESS, pid, ioprio | (clazz << CLASS_SHIFT))) { + if (syscall(SYS_ioprio_set, IOPRIO_WHO_PROCESS, pid, ioprio | (clazz << IOPRIO_CLASS_SHIFT))) { return -1; } #endif return 0; } -int android_get_ioprio(int pid, IoSchedClass *clazz, int *ioprio) { +int android_get_ioprio(int pid __android_unused, IoSchedClass *clazz, int *ioprio) { #ifdef HAVE_ANDROID_OS int rc; - if ((rc = ioprio_get(WHO_PROCESS, pid)) < 0) { + if ((rc = syscall(SYS_ioprio_get, IOPRIO_WHO_PROCESS, pid)) < 0) { return -1; } - *clazz = (rc >> CLASS_SHIFT); + *clazz = (rc >> IOPRIO_CLASS_SHIFT); *ioprio = (rc & 0xff); #else *clazz = IoSchedClass_NONE; @@ -66,5 +56,3 @@ int android_get_ioprio(int pid, IoSchedClass *clazz, int *ioprio) { #endif return 0; } - -#endif /* HAVE_SCHED_H */ diff --git a/libcutils/klog.c b/libcutils/klog.c index d69fb10..fbb7b72 100644 --- a/libcutils/klog.c +++ b/libcutils/klog.c @@ -28,6 +28,10 @@ static int klog_fd = -1; static int klog_level = KLOG_DEFAULT_LEVEL; +int klog_get_level(void) { + return klog_level; +} + void klog_set_level(int level) { klog_level = level; } @@ -49,18 +53,24 @@ void klog_init(void) #define LOG_BUF_MAX 512 -void klog_write(int level, const char *fmt, ...) +void klog_vwrite(int level, const char *fmt, va_list ap) { char buf[LOG_BUF_MAX]; - va_list ap; if (level > klog_level) return; if (klog_fd < 0) klog_init(); if (klog_fd < 0) return; - va_start(ap, fmt); vsnprintf(buf, LOG_BUF_MAX, fmt, ap); buf[LOG_BUF_MAX - 1] = 0; - va_end(ap); + write(klog_fd, buf, strlen(buf)); } + +void klog_write(int level, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + klog_vwrite(level, fmt, ap); + va_end(ap); +} diff --git a/libcutils/partition_utils.c b/libcutils/partition_utils.c index 10539fa..823b162 100644 --- a/libcutils/partition_utils.c +++ b/libcutils/partition_utils.c @@ -14,12 +14,13 @@ * limitations under the License. */ -#include <sys/types.h> -#include <unistd.h> #include <fcntl.h> -#include <sys/stat.h> #include <sys/ioctl.h> #include <sys/mount.h> /* for BLKGETSIZE */ +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + #include <cutils/properties.h> static int only_one_char(char *buf, int len, char c) @@ -39,7 +40,7 @@ static int only_one_char(char *buf, int len, char c) int partition_wiped(char *source) { char buf[4096]; - int fd, ret, wiped; + int fd, ret; if ((fd = open(source, O_RDONLY)) < 0) { return 0; diff --git a/libcutils/process_name.c b/libcutils/process_name.c index a6ab951..9c3dfb8 100644 --- a/libcutils/process_name.c +++ b/libcutils/process_name.c @@ -14,25 +14,27 @@ * limitations under the License. */ +#include <fcntl.h> #include <stdlib.h> #include <string.h> -#include <cutils/process_name.h> -#ifdef HAVE_ANDROID_OS -#include <cutils/properties.h> +#if defined(HAVE_PRCTL) +#include <sys/prctl.h> #endif -#include <unistd.h> -#include <sys/types.h> #include <sys/stat.h> -#include <fcntl.h> +#include <sys/types.h> +#include <unistd.h> -#if defined(HAVE_PRCTL) -#include <sys/prctl.h> +#include <cutils/process_name.h> +#ifdef HAVE_ANDROID_OS +#include <cutils/properties.h> #endif #define PROCESS_NAME_DEVICE "/sys/qemu_trace/process_name" static const char* process_name = "unknown"; +#ifdef HAVE_ANDROID_OS static int running_in_emulator = -1; +#endif void set_process_name(const char* new_name) { #ifdef HAVE_ANDROID_OS diff --git a/libcutils/properties.c b/libcutils/properties.c index 28d8b2f..b283658 100644 --- a/libcutils/properties.c +++ b/libcutils/properties.c @@ -15,17 +15,95 @@ */ #define LOG_TAG "properties" +// #define LOG_NDEBUG 0 #include <stdlib.h> #include <string.h> +#include <ctype.h> #include <unistd.h> #include <cutils/sockets.h> #include <errno.h> #include <assert.h> #include <cutils/properties.h> +#include <stdbool.h> +#include <inttypes.h> #include "loghack.h" +int8_t property_get_bool(const char *key, int8_t default_value) { + if (!key) { + return default_value; + } + + int8_t result = default_value; + char buf[PROPERTY_VALUE_MAX] = {'\0',}; + + int len = property_get(key, buf, ""); + if (len == 1) { + char ch = buf[0]; + if (ch == '0' || ch == 'n') { + result = false; + } else if (ch == '1' || ch == 'y') { + result = true; + } + } else if (len > 1) { + if (!strcmp(buf, "no") || !strcmp(buf, "false") || !strcmp(buf, "off")) { + result = false; + } else if (!strcmp(buf, "yes") || !strcmp(buf, "true") || !strcmp(buf, "on")) { + result = true; + } + } + + return result; +} + +// Convert string property to int (default if fails); return default value if out of bounds +static intmax_t property_get_imax(const char *key, intmax_t lower_bound, intmax_t upper_bound, + intmax_t default_value) { + if (!key) { + return default_value; + } + + intmax_t result = default_value; + char buf[PROPERTY_VALUE_MAX] = {'\0',}; + char *end = NULL; + + int len = property_get(key, buf, ""); + if (len > 0) { + int tmp = errno; + errno = 0; + + // Infer base automatically + result = strtoimax(buf, &end, /*base*/0); + if ((result == INTMAX_MIN || result == INTMAX_MAX) && errno == ERANGE) { + // Over or underflow + result = default_value; + ALOGV("%s(%s,%" PRIdMAX ") - overflow", __FUNCTION__, key, default_value); + } else if (result < lower_bound || result > upper_bound) { + // Out of range of requested bounds + result = default_value; + ALOGV("%s(%s,%" PRIdMAX ") - out of range", __FUNCTION__, key, default_value); + } else if (end == buf) { + // Numeric conversion failed + result = default_value; + ALOGV("%s(%s,%" PRIdMAX ") - numeric conversion failed", + __FUNCTION__, key, default_value); + } + + errno = tmp; + } + + return result; +} + +int64_t property_get_int64(const char *key, int64_t default_value) { + return (int64_t)property_get_imax(key, INT64_MIN, INT64_MAX, default_value); +} + +int32_t property_get_int32(const char *key, int32_t default_value) { + return (int32_t)property_get_imax(key, INT32_MIN, INT32_MAX, default_value); +} + #ifdef HAVE_LIBC_SYSTEM_PROPERTIES #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_ @@ -44,10 +122,13 @@ int property_get(const char *key, char *value, const char *default_value) if(len > 0) { return len; } - if(default_value) { len = strlen(default_value); - memcpy(value, default_value, len + 1); + if (len >= PROPERTY_VALUE_MAX) { + len = PROPERTY_VALUE_MAX - 1; + } + memcpy(value, default_value, len); + value[len] = '\0'; } return len; } diff --git a/libcutils/qtaguid.c b/libcutils/qtaguid.c index 5bb8176..00e211c 100644 --- a/libcutils/qtaguid.c +++ b/libcutils/qtaguid.c @@ -1,5 +1,4 @@ -/* libcutils/qtaguid.c -** +/* ** Copyright 2011, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,14 +18,16 @@ #define LOG_TAG "qtaguid" -#include <cutils/qtaguid.h> -#include <cutils/log.h> #include <errno.h> #include <fcntl.h> +#include <inttypes.h> +#include <pthread.h> #include <stdio.h> #include <string.h> #include <unistd.h> -#include <pthread.h> + +#include <cutils/qtaguid.h> +#include <log/log.h> static const char* CTRL_PROCPATH = "/proc/net/xt_qtaguid/ctrl"; static const int CTRL_MAX_INPUT_LEN = 128; @@ -103,13 +104,13 @@ int qtaguid_tagSocket(int sockfd, int tag, uid_t uid) { pthread_once(&resTrackInitDone, qtaguid_resTrack); - snprintf(lineBuf, sizeof(lineBuf), "t %d %llu %d", sockfd, kTag, uid); + snprintf(lineBuf, sizeof(lineBuf), "t %d %" PRIu64 " %d", sockfd, kTag, uid); - ALOGV("Tagging socket %d with tag %llx{%u,0} for uid %d", sockfd, kTag, tag, uid); + ALOGV("Tagging socket %d with tag %" PRIx64 "{%u,0} for uid %d", sockfd, kTag, tag, uid); res = write_ctrl(lineBuf); if (res < 0) { - ALOGI("Tagging socket %d with tag %llx(%d) for uid %d failed errno=%d", + ALOGI("Tagging socket %d with tag %" PRIx64 "(%d) for uid %d failed errno=%d", sockfd, kTag, tag, uid, res); } @@ -144,17 +145,17 @@ int qtaguid_setCounterSet(int counterSetNum, uid_t uid) { int qtaguid_deleteTagData(int tag, uid_t uid) { char lineBuf[CTRL_MAX_INPUT_LEN]; - int fd, cnt = 0, res = 0; + int cnt = 0, res = 0; uint64_t kTag = (uint64_t)tag << 32; - ALOGV("Deleting tag data with tag %llx{%d,0} for uid %d", kTag, tag, uid); + ALOGV("Deleting tag data with tag %" PRIx64 "{%d,0} for uid %d", kTag, tag, uid); pthread_once(&resTrackInitDone, qtaguid_resTrack); - snprintf(lineBuf, sizeof(lineBuf), "d %llu %d", kTag, uid); + snprintf(lineBuf, sizeof(lineBuf), "d %" PRIu64 " %d", kTag, uid); res = write_ctrl(lineBuf); if (res < 0) { - ALOGI("Deleteing tag data with tag %llx/%d for uid %d failed with cnt=%d errno=%d", + ALOGI("Deleting tag data with tag %" PRIx64 "/%d for uid %d failed with cnt=%d errno=%d", kTag, tag, uid, cnt, errno); } @@ -162,8 +163,6 @@ int qtaguid_deleteTagData(int tag, uid_t uid) { } int qtaguid_setPacifier(int on) { - int param_fd; - int res; const char *value; value = on ? "Y" : "N"; diff --git a/libcutils/sched_policy.c b/libcutils/sched_policy.c index d20d217..9f092d6 100644 --- a/libcutils/sched_policy.c +++ b/libcutils/sched_policy.c @@ -1,6 +1,4 @@ - -/* libs/cutils/sched_policy.c -** +/* ** Copyright 2007, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,14 +16,17 @@ #define LOG_TAG "SchedPolicy" +#include <errno.h> +#include <fcntl.h> #include <stdio.h> #include <stdlib.h> -#include <unistd.h> #include <string.h> -#include <errno.h> -#include <fcntl.h> +#include <unistd.h> + #include <cutils/sched_policy.h> -#include <cutils/log.h> +#include <log/log.h> + +#define UNUSED __attribute__((__unused__)) /* Re-map SP_DEFAULT to the system default policy, and leave other values unchanged. * Call this any place a SchedPolicy is used as an input parameter. @@ -331,12 +332,12 @@ int set_sched_policy(int tid, SchedPolicy policy) /* Stubs for non-Android targets. */ -int set_sched_policy(int tid, SchedPolicy policy) +int set_sched_policy(int tid UNUSED, SchedPolicy policy UNUSED) { return 0; } -int get_sched_policy(int tid, SchedPolicy *policy) +int get_sched_policy(int tid UNUSED, SchedPolicy *policy) { *policy = SP_SYSTEM_DEFAULT; return 0; diff --git a/libcutils/socket_inaddr_any_server.c b/libcutils/socket_inaddr_any_server.c index 7d5dab4..6c849de 100644 --- a/libcutils/socket_inaddr_any_server.c +++ b/libcutils/socket_inaddr_any_server.c @@ -1,5 +1,4 @@ -/* libs/cutils/socket_inaddr_any_server.c -** +/* ** Copyright 2006, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,13 +14,11 @@ ** limitations under the License. */ -#include <cutils/sockets.h> - +#include <errno.h> +#include <stddef.h> #include <stdlib.h> #include <string.h> #include <unistd.h> -#include <errno.h> -#include <stddef.h> #ifndef HAVE_WINSOCK #include <sys/socket.h> @@ -30,13 +27,14 @@ #include <netinet/in.h> #endif +#include <cutils/sockets.h> + #define LISTEN_BACKLOG 4 /* open listen() port on any interface */ int socket_inaddr_any_server(int port, int type) { struct sockaddr_in addr; - size_t alen; int s, n; memset(&addr, 0, sizeof(addr)); @@ -48,7 +46,7 @@ int socket_inaddr_any_server(int port, int type) if(s < 0) return -1; n = 1; - setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n)); + setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *) &n, sizeof(n)); if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { close(s); diff --git a/libcutils/socket_local_client.c b/libcutils/socket_local_client.c index 5310516..ddcc2da 100644 --- a/libcutils/socket_local_client.c +++ b/libcutils/socket_local_client.c @@ -14,13 +14,13 @@ * limitations under the License. */ -#include <cutils/sockets.h> - +#include <errno.h> +#include <stddef.h> #include <stdlib.h> #include <string.h> #include <unistd.h> -#include <errno.h> -#include <stddef.h> + +#include <cutils/sockets.h> #ifdef HAVE_WINSOCK @@ -128,7 +128,6 @@ int socket_local_client_connect(int fd, const char *name, int namespaceId, { struct sockaddr_un addr; socklen_t alen; - size_t namelen; int err; err = socket_make_sockaddr_un(name, namespaceId, &addr, &alen); diff --git a/libcutils/socket_local_server.c b/libcutils/socket_local_server.c index 4971b1b..7628fe4 100644 --- a/libcutils/socket_local_server.c +++ b/libcutils/socket_local_server.c @@ -43,6 +43,8 @@ int socket_local_server(const char *name, int namespaceId, int type) #define LISTEN_BACKLOG 4 +/* Only the bottom bits are really the socket type; there are flags too. */ +#define SOCK_TYPE_MASK 0xf /** * Binds a pre-created socket(AF_LOCAL) 's' to 'name' @@ -107,7 +109,7 @@ int socket_local_server(const char *name, int namespace, int type) return -1; } - if (type == SOCK_STREAM) { + if ((type & SOCK_TYPE_MASK) == SOCK_STREAM) { int ret; ret = listen(s, LISTEN_BACKLOG); diff --git a/libcutils/socket_loopback_client.c b/libcutils/socket_loopback_client.c index cb82c5e..9aed7b7 100644 --- a/libcutils/socket_loopback_client.c +++ b/libcutils/socket_loopback_client.c @@ -1,5 +1,4 @@ -/* libs/cutils/socket_loopback_client.c -** +/* ** Copyright 2006, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,13 +14,11 @@ ** limitations under the License. */ -#include <cutils/sockets.h> - +#include <errno.h> +#include <stddef.h> #include <stdlib.h> #include <string.h> #include <unistd.h> -#include <errno.h> -#include <stddef.h> #ifndef HAVE_WINSOCK #include <sys/socket.h> @@ -30,6 +27,8 @@ #include <netinet/in.h> #endif +#include <cutils/sockets.h> + /* Connect to port on the loopback IP interface. type is * SOCK_STREAM or SOCK_DGRAM. * return is a file descriptor or -1 on error @@ -37,7 +36,6 @@ int socket_loopback_client(int port, int type) { struct sockaddr_in addr; - socklen_t alen; int s; memset(&addr, 0, sizeof(addr)); diff --git a/libcutils/socket_loopback_server.c b/libcutils/socket_loopback_server.c index 3208488..71afce7 100644 --- a/libcutils/socket_loopback_server.c +++ b/libcutils/socket_loopback_server.c @@ -1,5 +1,4 @@ -/* libs/cutils/socket_loopback_server.c -** +/* ** Copyright 2006, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,13 +14,11 @@ ** limitations under the License. */ -#include <cutils/sockets.h> - +#include <errno.h> +#include <stddef.h> #include <stdlib.h> #include <string.h> #include <unistd.h> -#include <errno.h> -#include <stddef.h> #define LISTEN_BACKLOG 4 @@ -32,11 +29,12 @@ #include <netinet/in.h> #endif +#include <cutils/sockets.h> + /* open listen() port on loopback interface */ int socket_loopback_server(int port, int type) { struct sockaddr_in addr; - size_t alen; int s, n; memset(&addr, 0, sizeof(addr)); @@ -48,7 +46,7 @@ int socket_loopback_server(int port, int type) if(s < 0) return -1; n = 1; - setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n)); + setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *) &n, sizeof(n)); if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { close(s); diff --git a/libcutils/socket_network_client.c b/libcutils/socket_network_client.c index a64006c..4826033 100644 --- a/libcutils/socket_network_client.c +++ b/libcutils/socket_network_client.c @@ -1,5 +1,4 @@ -/* libs/cutils/socket_network_client.c -** +/* ** Copyright 2006, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,22 +14,20 @@ ** limitations under the License. */ -#include <cutils/sockets.h> - +#include <errno.h> +#include <fcntl.h> +#include <stddef.h> #include <stdlib.h> #include <string.h> #include <unistd.h> -#include <errno.h> -#include <stddef.h> -#ifndef HAVE_WINSOCK #include <sys/socket.h> #include <sys/select.h> #include <sys/types.h> #include <netinet/in.h> #include <netdb.h> -#endif +#include <cutils/sockets.h> /* Connect to port on the IP interface. type is * SOCK_STREAM or SOCK_DGRAM. @@ -38,28 +35,92 @@ */ int socket_network_client(const char *host, int port, int type) { + return socket_network_client_timeout(host, port, type, 0); +} + +/* Connect to port on the IP interface. type is SOCK_STREAM or SOCK_DGRAM. + * timeout in seconds return is a file descriptor or -1 on error + */ +int socket_network_client_timeout(const char *host, int port, int type, int timeout) +{ struct hostent *hp; struct sockaddr_in addr; socklen_t alen; int s; + int flags = 0, error = 0, ret = 0; + fd_set rset, wset; + socklen_t len = sizeof(error); + struct timeval ts; + + ts.tv_sec = timeout; + ts.tv_usec = 0; hp = gethostbyname(host); - if(hp == 0) return -1; - + if (hp == 0) return -1; + memset(&addr, 0, sizeof(addr)); addr.sin_family = hp->h_addrtype; addr.sin_port = htons(port); memcpy(&addr.sin_addr, hp->h_addr, hp->h_length); s = socket(hp->h_addrtype, type, 0); - if(s < 0) return -1; + if (s < 0) return -1; - if(connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + if ((flags = fcntl(s, F_GETFL, 0)) < 0) { close(s); return -1; } - return s; + if (fcntl(s, F_SETFL, flags | O_NONBLOCK) < 0) { + close(s); + return -1; + } -} + if ((ret = connect(s, (struct sockaddr *) &addr, sizeof(addr))) < 0) { + if (errno != EINPROGRESS) { + close(s); + return -1; + } + } + + if (ret == 0) + goto done; + + FD_ZERO(&rset); + FD_SET(s, &rset); + wset = rset; + if ((ret = select(s + 1, &rset, &wset, NULL, (timeout) ? &ts : NULL)) < 0) { + close(s); + return -1; + } + if (ret == 0) { // we had a timeout + errno = ETIMEDOUT; + close(s); + return -1; + } + + if (FD_ISSET(s, &rset) || FD_ISSET(s, &wset)) { + if (getsockopt(s, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { + close(s); + return -1; + } + } else { + close(s); + return -1; + } + + if (error) { // check if we had a socket error + errno = error; + close(s); + return -1; + } + +done: + if (fcntl(s, F_SETFL, flags) < 0) { + close(s); + return -1; + } + + return s; +} diff --git a/libcutils/sockets.c b/libcutils/sockets.c index b5a1b3d..15ede2b 100644 --- a/libcutils/sockets.c +++ b/libcutils/sockets.c @@ -14,15 +14,18 @@ * limitations under the License. */ -#include <cutils/log.h> #include <cutils/sockets.h> +#include <log/log.h> #ifdef HAVE_ANDROID_OS /* For the socket trust (credentials) check */ #include <private/android_filesystem_config.h> +#define __android_unused +#else +#define __android_unused __attribute__((__unused__)) #endif -bool socket_peer_is_trusted(int fd) +bool socket_peer_is_trusted(int fd __android_unused) { #ifdef HAVE_ANDROID_OS struct ucred cr; diff --git a/libcutils/str_parms.c b/libcutils/str_parms.c index 7cfbcb3..2e3ce9f 100644 --- a/libcutils/str_parms.c +++ b/libcutils/str_parms.c @@ -25,10 +25,9 @@ #include <string.h> #include <cutils/hashmap.h> -#include <cutils/log.h> #include <cutils/memory.h> - #include <cutils/str_parms.h> +#include <log/log.h> #define UNUSED __attribute__((unused)) @@ -194,23 +193,46 @@ err_create_str_parms: int str_parms_add_str(struct str_parms *str_parms, const char *key, const char *value) { - void *old_val; - void *tmp_key; - void *tmp_val; + void *tmp_key = NULL; + void *tmp_val = NULL; + void *old_val = NULL; + + // strdup and hashmapPut both set errno on failure. + // Set errno to 0 so we can recognize whether anything went wrong. + int saved_errno = errno; + errno = 0; tmp_key = strdup(key); + if (tmp_key == NULL) { + goto clean_up; + } + tmp_val = strdup(value); - old_val = hashmapPut(str_parms->map, tmp_key, tmp_val); + if (tmp_val == NULL) { + goto clean_up; + } - if (old_val) { - free(old_val); - free(tmp_key); - } else if (errno == ENOMEM) { - free(tmp_key); - free(tmp_val); - return -ENOMEM; + old_val = hashmapPut(str_parms->map, tmp_key, tmp_val); + if (old_val == NULL) { + // Did hashmapPut fail? + if (errno == ENOMEM) { + goto clean_up; + } + // For new keys, hashmap takes ownership of tmp_key and tmp_val. + tmp_key = tmp_val = NULL; + } else { + // For existing keys, hashmap takes ownership of tmp_val. + // (It also gives up ownership of old_val.) + tmp_val = NULL; } - return 0; + +clean_up: + free(tmp_key); + free(tmp_val); + free(old_val); + int result = -errno; + errno = saved_errno; + return result; } int str_parms_add_int(struct str_parms *str_parms, const char *key, int value) @@ -337,7 +359,6 @@ static void test_str_parms_str(const char *str) { struct str_parms *str_parms; char *out_str; - int ret; str_parms = str_parms_create_str(str); str_parms_add_str(str_parms, "dude", "woah"); @@ -352,8 +373,6 @@ static void test_str_parms_str(const char *str) int main(void) { - struct str_parms *str_parms; - test_str_parms_str(""); test_str_parms_str(";"); test_str_parms_str("="); @@ -370,6 +389,15 @@ int main(void) test_str_parms_str("foo=bar;baz=bat;"); test_str_parms_str("foo=bar;baz=bat;foo=bar"); + // hashmapPut reports errors by setting errno to ENOMEM. + // Test that we're not confused by running in an environment where this is already true. + errno = ENOMEM; + test_str_parms_str("foo=bar;baz="); + if (errno != ENOMEM) { + abort(); + } + test_str_parms_str("foo=bar;baz="); + return 0; } #endif diff --git a/libcutils/tests/Android.mk b/libcutils/tests/Android.mk index 6571161..8e65310 100644 --- a/libcutils/tests/Android.mk +++ b/libcutils/tests/Android.mk @@ -1 +1,48 @@ -include $(all-subdir-makefiles) +# Copyright (C) 2014 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. + +LOCAL_PATH := $(call my-dir) + +test_src_files := \ + MemsetTest.cpp \ + PropertiesTest.cpp \ + +include $(CLEAR_VARS) +LOCAL_MODULE := libcutils_test +LOCAL_SRC_FILES := $(test_src_files) +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + liblog \ + libutils \ + +LOCAL_MULTILIB := both +LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32 +LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64 +include $(BUILD_NATIVE_TEST) + +include $(CLEAR_VARS) +LOCAL_MODULE := libcutils_test_static +LOCAL_FORCE_STATIC_EXECUTABLE := true +LOCAL_SRC_FILES := $(test_src_files) +LOCAL_STATIC_LIBRARIES := \ + libc \ + libcutils \ + liblog \ + libstlport_static \ + libutils \ + +LOCAL_MULTILIB := both +LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32 +LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64 +include $(BUILD_NATIVE_TEST) diff --git a/libcutils/tests/MemsetTest.cpp b/libcutils/tests/MemsetTest.cpp new file mode 100644 index 0000000..45efc51 --- /dev/null +++ b/libcutils/tests/MemsetTest.cpp @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2014 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 <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <sys/mman.h> +#include <sys/types.h> + +#include <cutils/memory.h> +#include <gtest/gtest.h> + +#define FENCEPOST_LENGTH 8 + +#define MAX_TEST_SIZE (64*1024) +// Choose values that have no repeating byte values. +#define MEMSET16_PATTERN 0xb139 +#define MEMSET32_PATTERN 0x48193a27 + +enum test_e { + MEMSET16 = 0, + MEMSET32, +}; + +static int g_memset16_aligns[][2] = { + { 2, 0 }, + { 4, 0 }, + { 8, 0 }, + { 16, 0 }, + { 32, 0 }, + { 64, 0 }, + { 128, 0 }, + + { 4, 2 }, + + { 8, 2 }, + { 8, 4 }, + { 8, 6 }, + + { 128, 2 }, + { 128, 4 }, + { 128, 6 }, + { 128, 8 }, + { 128, 10 }, + { 128, 12 }, + { 128, 14 }, + { 128, 16 }, +}; + +static int g_memset32_aligns[][2] = { + { 4, 0 }, + { 8, 0 }, + { 16, 0 }, + { 32, 0 }, + { 64, 0 }, + { 128, 0 }, + + { 8, 4 }, + + { 128, 4 }, + { 128, 8 }, + { 128, 12 }, + { 128, 16 }, +}; + +static size_t GetIncrement(size_t len, size_t min_incr) { + if (len >= 4096) { + return 1024; + } else if (len >= 1024) { + return 256; + } + return min_incr; +} + +// Return a pointer into the current buffer with the specified alignment. +static void *GetAlignedPtr(void *orig_ptr, int alignment, int or_mask) { + uint64_t ptr = reinterpret_cast<uint64_t>(orig_ptr); + if (alignment > 0) { + // When setting the alignment, set it to exactly the alignment chosen. + // The pointer returned will be guaranteed not to be aligned to anything + // more than that. + ptr += alignment - (ptr & (alignment - 1)); + ptr |= alignment | or_mask; + } + + return reinterpret_cast<void*>(ptr); +} + +static void SetFencepost(uint8_t *buffer) { + for (int i = 0; i < FENCEPOST_LENGTH; i += 2) { + buffer[i] = 0xde; + buffer[i+1] = 0xad; + } +} + +static void VerifyFencepost(uint8_t *buffer) { + for (int i = 0; i < FENCEPOST_LENGTH; i += 2) { + if (buffer[i] != 0xde || buffer[i+1] != 0xad) { + uint8_t expected_value; + if (buffer[i] == 0xde) { + i++; + expected_value = 0xad; + } else { + expected_value = 0xde; + } + ASSERT_EQ(expected_value, buffer[i]); + } + } +} + +void RunMemsetTests(test_e test_type, uint32_t value, int align[][2], size_t num_aligns) { + size_t min_incr = 4; + if (test_type == MEMSET16) { + min_incr = 2; + value |= value << 16; + } + uint32_t* expected_buf = new uint32_t[MAX_TEST_SIZE/sizeof(uint32_t)]; + for (size_t i = 0; i < MAX_TEST_SIZE/sizeof(uint32_t); i++) { + expected_buf[i] = value; + } + + // Allocate one large buffer with lots of extra space so that we can + // guarantee that all possible alignments will fit. + uint8_t *buf = new uint8_t[3*MAX_TEST_SIZE]; + uint8_t *buf_align; + for (size_t i = 0; i < num_aligns; i++) { + size_t incr = min_incr; + for (size_t len = incr; len <= MAX_TEST_SIZE; len += incr) { + incr = GetIncrement(len, min_incr); + + buf_align = reinterpret_cast<uint8_t*>(GetAlignedPtr( + buf+FENCEPOST_LENGTH, align[i][0], align[i][1])); + + SetFencepost(&buf_align[-FENCEPOST_LENGTH]); + SetFencepost(&buf_align[len]); + + memset(buf_align, 0xff, len); + if (test_type == MEMSET16) { + android_memset16(reinterpret_cast<uint16_t*>(buf_align), value, len); + } else { + android_memset32(reinterpret_cast<uint32_t*>(buf_align), value, len); + } + ASSERT_EQ(0, memcmp(expected_buf, buf_align, len)) + << "Failed size " << len << " align " << align[i][0] << " " << align[i][1] << "\n"; + + VerifyFencepost(&buf_align[-FENCEPOST_LENGTH]); + VerifyFencepost(&buf_align[len]); + } + } + delete expected_buf; + delete buf; +} + +TEST(libcutils, android_memset16_non_zero) { + RunMemsetTests(MEMSET16, MEMSET16_PATTERN, g_memset16_aligns, sizeof(g_memset16_aligns)/sizeof(int[2])); +} + +TEST(libcutils, android_memset16_zero) { + RunMemsetTests(MEMSET16, 0, g_memset16_aligns, sizeof(g_memset16_aligns)/sizeof(int[2])); +} + +TEST(libcutils, android_memset32_non_zero) { + RunMemsetTests(MEMSET32, MEMSET32_PATTERN, g_memset32_aligns, sizeof(g_memset32_aligns)/sizeof(int[2])); +} + +TEST(libcutils, android_memset32_zero) { + RunMemsetTests(MEMSET32, 0, g_memset32_aligns, sizeof(g_memset32_aligns)/sizeof(int[2])); +} diff --git a/libcutils/tests/PropertiesTest.cpp b/libcutils/tests/PropertiesTest.cpp new file mode 100644 index 0000000..659821c --- /dev/null +++ b/libcutils/tests/PropertiesTest.cpp @@ -0,0 +1,309 @@ +/* + * Copyright (C) 2014 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. + */ + +#define LOG_TAG "Properties_test" +#include <utils/Log.h> +#include <gtest/gtest.h> + +#include <cutils/properties.h> +#include <limits.h> +#include <string> +#include <sstream> +#include <iostream> + +namespace android { + +#define STRINGIFY_INNER(x) #x +#define STRINGIFY(x) STRINGIFY_INNER(x) +#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0])) +#define ASSERT_OK(x) ASSERT_EQ(0, (x)) +#define EXPECT_OK(x) EXPECT_EQ(0, (x)) + +#define PROPERTY_TEST_KEY "libcutils.test.key" +#define PROPERTY_TEST_VALUE_DEFAULT "<<<default_value>>>" + +template <typename T> +static std::string HexString(T value) { + std::stringstream ss; + ss << "0x" << std::hex << std::uppercase << value; + return ss.str(); +} + +template <typename T> +static ::testing::AssertionResult AssertEqualHex(const char *mExpr, + const char *nExpr, + T m, + T n) { + if (m == n) { + return ::testing::AssertionSuccess(); + } + + return ::testing::AssertionFailure() + << mExpr << " and " << nExpr << " (expected: " << HexString(m) << + ", actual: " << HexString(n) << ") are not equal"; +} + +class PropertiesTest : public testing::Test { +public: + PropertiesTest() : mValue() {} +protected: + virtual void SetUp() { + EXPECT_OK(property_set(PROPERTY_TEST_KEY, /*value*/NULL)); + } + + virtual void TearDown() { + EXPECT_OK(property_set(PROPERTY_TEST_KEY, /*value*/NULL)); + } + + char mValue[PROPERTY_VALUE_MAX]; + + template <typename T> + static std::string ToString(T value) { + std::stringstream ss; + ss << value; + + return ss.str(); + } + + // Return length of property read; value is written into mValue + int SetAndGetProperty(const char* value, const char* defaultValue = PROPERTY_TEST_VALUE_DEFAULT) { + EXPECT_OK(property_set(PROPERTY_TEST_KEY, value)) << "value: '" << value << "'"; + return property_get(PROPERTY_TEST_KEY, mValue, defaultValue); + } + + void ResetValue(unsigned char c = 0xFF) { + for (size_t i = 0; i < ARRAY_SIZE(mValue); ++i) { + mValue[i] = (char) c; + } + } +}; + +TEST_F(PropertiesTest, SetString) { + + // Null key -> unsuccessful set + { + // Null key -> fails + EXPECT_GT(0, property_set(/*key*/NULL, PROPERTY_TEST_VALUE_DEFAULT)); + } + + // Null value -> returns default value + { + // Null value -> OK , and it clears the value + EXPECT_OK(property_set(PROPERTY_TEST_KEY, /*value*/NULL)); + ResetValue(); + + // Since the value is null, default value will be returned + int len = property_get(PROPERTY_TEST_KEY, mValue, PROPERTY_TEST_VALUE_DEFAULT); + EXPECT_EQ(strlen(PROPERTY_TEST_VALUE_DEFAULT), len); + EXPECT_STREQ(PROPERTY_TEST_VALUE_DEFAULT, mValue); + } + + // Trivial case => get returns what was set + { + int len = SetAndGetProperty("hello_world"); + EXPECT_EQ(strlen("hello_world"), len) << "hello_world key"; + EXPECT_STREQ("hello_world", mValue); + ResetValue(); + } + + // Set to empty string => get returns default always + { + const char* EMPTY_STRING_DEFAULT = "EMPTY_STRING"; + int len = SetAndGetProperty("", EMPTY_STRING_DEFAULT); + EXPECT_EQ(strlen(EMPTY_STRING_DEFAULT), len) << "empty key"; + EXPECT_STREQ(EMPTY_STRING_DEFAULT, mValue); + ResetValue(); + } + + // Set to max length => get returns what was set + { + std::string maxLengthString = std::string(PROPERTY_VALUE_MAX-1, 'a'); + + int len = SetAndGetProperty(maxLengthString.c_str()); + EXPECT_EQ(PROPERTY_VALUE_MAX-1, len) << "max length key"; + EXPECT_STREQ(maxLengthString.c_str(), mValue); + ResetValue(); + } + + // Set to max length + 1 => set fails + { + const char* VALID_TEST_VALUE = "VALID_VALUE"; + ASSERT_OK(property_set(PROPERTY_TEST_KEY, VALID_TEST_VALUE)); + + std::string oneLongerString = std::string(PROPERTY_VALUE_MAX, 'a'); + + // Expect that the value set fails since it's too long + EXPECT_GT(0, property_set(PROPERTY_TEST_KEY, oneLongerString.c_str())); + int len = property_get(PROPERTY_TEST_KEY, mValue, PROPERTY_TEST_VALUE_DEFAULT); + + EXPECT_EQ(strlen(VALID_TEST_VALUE), len) << "set should've failed"; + EXPECT_STREQ(VALID_TEST_VALUE, mValue); + ResetValue(); + } +} + +TEST_F(PropertiesTest, GetString) { + + // Try to use a default value that's too long => set fails + { + ASSERT_OK(property_set(PROPERTY_TEST_KEY, "")); + + std::string maxLengthString = std::string(PROPERTY_VALUE_MAX-1, 'a'); + std::string oneLongerString = std::string(PROPERTY_VALUE_MAX, 'a'); + + // Expect that the value is truncated since it's too long (by 1) + int len = property_get(PROPERTY_TEST_KEY, mValue, oneLongerString.c_str()); + EXPECT_EQ(PROPERTY_VALUE_MAX-1, len); + EXPECT_STREQ(maxLengthString.c_str(), mValue); + ResetValue(); + } +} + +TEST_F(PropertiesTest, GetBool) { + /** + * TRUE + */ + const char *valuesTrue[] = { "1", "true", "y", "yes", "on", }; + for (size_t i = 0; i < ARRAY_SIZE(valuesTrue); ++i) { + ASSERT_OK(property_set(PROPERTY_TEST_KEY, valuesTrue[i])); + bool val = property_get_bool(PROPERTY_TEST_KEY, /*default_value*/false); + EXPECT_TRUE(val) << "Property should've been TRUE for value: '" << valuesTrue[i] << "'"; + } + + /** + * FALSE + */ + const char *valuesFalse[] = { "0", "false", "n", "no", "off", }; + for (size_t i = 0; i < ARRAY_SIZE(valuesFalse); ++i) { + ASSERT_OK(property_set(PROPERTY_TEST_KEY, valuesFalse[i])); + bool val = property_get_bool(PROPERTY_TEST_KEY, /*default_value*/true); + EXPECT_FALSE(val) << "Property shoud've been FALSE For string value: '" << valuesFalse[i] << "'"; + } + + /** + * NEITHER + */ + const char *valuesNeither[] = { "x0", "x1", "2", "-2", "True", "False", "garbage", "", " ", + "+1", " 1 ", " true", " true ", " y ", " yes", "yes ", + "+0", "-0", "00", " 00 ", " false", "false ", + }; + for (size_t i = 0; i < ARRAY_SIZE(valuesNeither); ++i) { + ASSERT_OK(property_set(PROPERTY_TEST_KEY, valuesNeither[i])); + + // The default value should always be used + bool val = property_get_bool(PROPERTY_TEST_KEY, /*default_value*/true); + EXPECT_TRUE(val) << "Property should've been NEITHER (true) for string value: '" << valuesNeither[i] << "'"; + + val = property_get_bool(PROPERTY_TEST_KEY, /*default_value*/false); + EXPECT_FALSE(val) << "Property should've been NEITHER (false) for string value: '" << valuesNeither[i] << "'"; + } +} + +TEST_F(PropertiesTest, GetInt64) { + const int64_t DEFAULT_VALUE = INT64_C(0xDEADBEEFBEEFDEAD); + + const std::string longMaxString = ToString(INT64_MAX); + const std::string longStringOverflow = longMaxString + "0"; + + const std::string longMinString = ToString(INT64_MIN); + const std::string longStringUnderflow = longMinString + "0"; + + const char* setValues[] = { + // base 10 + "1", "2", "12345", "-1", "-2", "-12345", + // base 16 + "0xFF", "0x0FF", "0xC0FFEE", + // base 8 + "0", "01234", "07", + // corner cases + " 2", "2 ", "+0", "-0", " +0 ", longMaxString.c_str(), longMinString.c_str(), + // failing cases + NULL, "", " ", " ", "hello", " true ", "y", + longStringOverflow.c_str(), longStringUnderflow.c_str(), + }; + + int64_t getValues[] = { + // base 10 + 1, 2, 12345, -1, -2, -12345, + // base 16 + 0xFF, 0x0FF, 0xC0FFEE, + // base 8 + 0, 01234, 07, + // corner cases + 2, 2, 0, 0, 0, INT64_MAX, INT64_MIN, + // failing cases + DEFAULT_VALUE, DEFAULT_VALUE, DEFAULT_VALUE, DEFAULT_VALUE, DEFAULT_VALUE, DEFAULT_VALUE, DEFAULT_VALUE, + DEFAULT_VALUE, DEFAULT_VALUE, + }; + + ASSERT_EQ(ARRAY_SIZE(setValues), ARRAY_SIZE(getValues)); + + for (size_t i = 0; i < ARRAY_SIZE(setValues); ++i) { + ASSERT_OK(property_set(PROPERTY_TEST_KEY, setValues[i])); + + int64_t val = property_get_int64(PROPERTY_TEST_KEY, DEFAULT_VALUE); + EXPECT_PRED_FORMAT2(AssertEqualHex, getValues[i], val) << "Property was set to '" << setValues[i] << "'"; + } +} + +TEST_F(PropertiesTest, GetInt32) { + const int32_t DEFAULT_VALUE = INT32_C(0xDEADBEEF); + + const std::string intMaxString = ToString(INT32_MAX); + const std::string intStringOverflow = intMaxString + "0"; + + const std::string intMinString = ToString(INT32_MIN); + const std::string intStringUnderflow = intMinString + "0"; + + const char* setValues[] = { + // base 10 + "1", "2", "12345", "-1", "-2", "-12345", + // base 16 + "0xFF", "0x0FF", "0xC0FFEE", "0Xf00", + // base 8 + "0", "01234", "07", + // corner cases + " 2", "2 ", "+0", "-0", " +0 ", intMaxString.c_str(), intMinString.c_str(), + // failing cases + NULL, "", " ", " ", "hello", " true ", "y", + intStringOverflow.c_str(), intStringUnderflow.c_str(), + }; + + int32_t getValues[] = { + // base 10 + 1, 2, 12345, -1, -2, -12345, + // base 16 + 0xFF, 0x0FF, 0xC0FFEE, 0Xf00, + // base 8 + 0, 01234, 07, + // corner cases + 2, 2, 0, 0, 0, INT32_MAX, INT32_MIN, + // failing cases + DEFAULT_VALUE, DEFAULT_VALUE, DEFAULT_VALUE, DEFAULT_VALUE, DEFAULT_VALUE, DEFAULT_VALUE, DEFAULT_VALUE, + DEFAULT_VALUE, DEFAULT_VALUE, + }; + + ASSERT_EQ(ARRAY_SIZE(setValues), ARRAY_SIZE(getValues)); + + for (size_t i = 0; i < ARRAY_SIZE(setValues); ++i) { + ASSERT_OK(property_set(PROPERTY_TEST_KEY, setValues[i])); + + int32_t val = property_get_int32(PROPERTY_TEST_KEY, DEFAULT_VALUE); + EXPECT_PRED_FORMAT2(AssertEqualHex, getValues[i], val) << "Property was set to '" << setValues[i] << "'"; + } +} + +} // namespace android diff --git a/libcutils/tests/memset_mips/Android.mk b/libcutils/tests/memset_mips/Android.mk deleted file mode 100644 index c22fca9..0000000 --- a/libcutils/tests/memset_mips/Android.mk +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright 2012 The Android Open Source Project - -ifeq ($(TARGET_ARCH),mips) - -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_SRC_FILES:= \ - test_memset.c \ - android_memset_dumb.S \ - android_memset_test.S \ - memset_cmips.S \ - memset_omips.S - -LOCAL_MODULE:= test_memset - -LOCAL_FORCE_STATIC_EXECUTABLE := true -LOCAL_STATIC_LIBRARIES := libcutils libc -LOCAL_MODULE_TAGS := tests - -include $(BUILD_EXECUTABLE) - -endif diff --git a/libcutils/tests/memset_mips/android_memset_dumb.S b/libcutils/tests/memset_mips/android_memset_dumb.S deleted file mode 100644 index c8a1a37..0000000 --- a/libcutils/tests/memset_mips/android_memset_dumb.S +++ /dev/null @@ -1,36 +0,0 @@ - .global android_memset16_dumb - .type android_memset16_dumb, @function -android_memset16_dumb: - .ent android_memset16_dumb - - .set noreorder - beqz $a2,9f - srl $a2,1 - -1: sh $a1,($a0) - subu $a2,1 - bnez $a2,1b - addu $a0,2 - .set reorder - -9: j $ra - .end android_memset16_dumb - .size android_memset16_dumb,.-android_memset16_dumb - - .global android_memset32_dumb - .type android_memset32_dumb, @function -android_memset32_dumb: - .ent android_memset32_dumb - .set noreorder - beqz $a2,9f - srl $a2,2 - -1: sw $a1,($a0) - subu $a2,1 - bnez $a2,1b - addu $a0,4 - .set reorder - -9: j $ra - .end android_memset32_dumb - .size android_memset32_dumb,.-android_memset32_dumb diff --git a/libcutils/tests/memset_mips/android_memset_test.S b/libcutils/tests/memset_mips/android_memset_test.S deleted file mode 100644 index e918843..0000000 --- a/libcutils/tests/memset_mips/android_memset_test.S +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (C) 2006 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. - */ - -#ifdef NDEBUG -#define DBG # -#else -#define DBG -#endif - - .text - .align - - /* - * Optimized memset16 for MIPS - * - * void android_memset16_test(uint16_t* dst, uint16_t value, size_t size); - * - */ - - .global android_memset16_test - .type android_memset16_test, @function -android_memset16_test: - .ent android_memset16_test - .set noreorder - - /* Check parameters */ -DBG andi $t0,$a0,1 /* $a0 must be halfword aligned */ -DBG tne $t0 -DBG lui $t1,0xffff /* $a1 must be 16bits */ -DBG and $t1,$a1 -DBG tne $t1 -DBG andi $t2,$a2,1 /* $a2 must be even */ -DBG tne $t2 - -#if (__mips==32) && (__mips_isa_rev>=2) - ins $a2,$0,0,1 -#else - li $t0,~1 - and $a2,$t0 -#endif - - move $t8,$ra - blez $a2,9f /* Anything to do? */ - andi $t0,$a0,2 /* Check dst alignment */ - /* Expand value to 32 bits and check destination alignment */ -#if (__mips==32) && (__mips_isa_rev>=2) - beqz $t0,.Laligned32 /* dst is 32 bit aligned */ - ins $a1,$a1,16,16 -#else - sll $t2,$a1,16 - beqz $t0,.Laligned32 /* dst is 32 bit aligned */ - or $a1,$t2 -#endif - sh $a1,($a0) /* do one halfword to get aligned */ - subu $a2,2 - addu $a0,2 - -.Laligned32: - and $t1,$a2,63 /* is there enough left to do a full 64 byte loop? */ - beq $a2,$t1,1f - subu $t2,$a2,$t1 /* $t2 is the number of bytes to do in loop64 */ - addu $t3,$a0,$t2 /* $t3 is the end marker for loop64 */ - subu $a2,$t2 -.Lloop64: - addu $a0,64 - sw $a1,-64($a0) - sw $a1,-60($a0) - sw $a1,-56($a0) - sw $a1,-52($a0) - sw $a1,-48($a0) - sw $a1,-44($a0) - sw $a1,-40($a0) - sw $a1,-36($a0) - sw $a1,-32($a0) - sw $a1,-28($a0) - sw $a1,-24($a0) - sw $a1,-20($a0) - sw $a1,-16($a0) - sw $a1,-12($a0) - sw $a1,-8($a0) - bne $a0,$t3,.Lloop64 - sw $a1,-4($a0) - - /* Do the last 0..62 bytes */ -1: li $t0,64+12 - andi $t1,$a2,0x3c /* $t1 how many bytes to store using sw */ - bal 1f - subu $t0,$t1 /* 64+12-$t0 is offset to jump from 1f */ -1: addu $ra,$t0 - j $ra - subu $a2,$t1 -2: sw $a1,60($a0) - sw $a1,56($a0) - sw $a1,52($a0) - sw $a1,48($a0) - sw $a1,44($a0) - sw $a1,40($a0) - sw $a1,36($a0) - sw $a1,32($a0) - sw $a1,28($a0) - sw $a1,24($a0) - sw $a1,20($a0) - sw $a1,16($a0) - sw $a1,12($a0) - sw $a1,8($a0) - sw $a1,4($a0) - sw $a1,0($a0) - - beqz $a2,9f - addu $a0,$t1 - sh $a1,($a0) - -9: j $t8 - nop - .end android_memset16_test - .size android_memset16_test,.-android_memset16_test - - /* - * Optimized memset32 for MIPS - * - * void android_memset32_test(uint32_t* dst, uint32_t value, size_t size); - * - */ - .global android_memset32_test - .type android_memset32_test, @function -android_memset32_test: - .ent android_memset32_test - .set noreorder - - /* Check parameters */ -DBG andi $t0,$a0,3 /* $a0 must be word aligned */ -DBG tne $t0 -DBG andi $t2,$a2,3 /* $a2 must be a multiple of 4 bytes */ -DBG tne $t2 - - b .Laligned32 - move $t8,$ra - .end android_memset32_test - .size android_memset32_test,.-android_memset32_test diff --git a/libcutils/tests/memset_mips/memset_cmips.S b/libcutils/tests/memset_mips/memset_cmips.S deleted file mode 100644 index f8f3a91..0000000 --- a/libcutils/tests/memset_mips/memset_cmips.S +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Copyright (c) 2009 - * MIPS Technologies, Inc., California. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the MIPS Technologies, Inc., nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE MIPS TECHNOLOGIES, INC. ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE MIPS TECHNOLOGIES, INC. BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -/************************************************************************ - * - * memset.S, version "64h" with 1 cache line horizon for "pref 30" and 14 nops - * Version: "043009" - * - ************************************************************************/ - - -/************************************************************************ - * Include files - ************************************************************************/ - -#include "machine/asm.h" - -/* - * This routine could be optimized for MIPS64. The current code only - * uses MIPS32 instructions. - */ - -#if defined(__MIPSEB__) -# define SWHI swl /* high part is left in big-endian */ -#endif - -#if defined(__MIPSEL__) -# define SWHI swr /* high part is right in little-endian */ -#endif - -#if !(defined(XGPROF) || defined(XPROF)) -#undef SETUP_GP -#define SETUP_GP -#endif - -LEAF(memset_cmips,0) - - .set noreorder - .set noat - - addu t0,a0,a2 # t0 is the "past the end" address - slti AT,a2,4 # is a2 less than 4? - bne AT,zero,.Llast4 # if yes, go to last4 - move v0,a0 # memset returns the dst pointer - - beq a1,zero,.Lset0 - subu v1,zero,a0 - - # smear byte into 32 bit word -#if (__mips==32) && (__mips_isa_rev>=2) - ins a1, a1, 8, 8 # Replicate fill byte into half-word. - ins a1, a1, 16, 16 # Replicate fill byte into word. -#else - and a1,0xff - sll AT,a1,8 - or a1,AT - sll AT,a1,16 - or a1,AT -#endif - -.Lset0: andi v1,v1,0x3 # word-unaligned address? - beq v1,zero,.Laligned # v1 is the unalignment count - subu a2,a2,v1 - SWHI a1,0(a0) - addu a0,a0,v1 - -# Here we have the "word-aligned" a0 (until the "last4") -.Laligned: - andi t8,a2,0x3f # any 64-byte chunks? - # t8 is the byte count past 64-byte chunks - beq a2,t8,.Lchk8w # when a2==t8, no 64-byte chunks - # There will be at most 1 32-byte chunk then - subu a3,a2,t8 # subtract from a2 the reminder - # Here a3 counts bytes in 16w chunks - addu a3,a0,a3 # Now a3 is the final dst after 64-byte chunks - -# Find out, if there are any 64-byte chunks after which will be still at least -# 96 bytes left. The value "96" is calculated as needed buffer for -# "pref 30,64(a0)" prefetch, which can be used as "pref 30,0(a0)" after -# incrementing "a0" by 64. -# For "a2" below 160 there will be no such "pref 30 safe" 64-byte chunk. -# - sltiu v1,a2,160 - bgtz v1,.Lloop16w_nopref30 # skip "pref 30,0(a0)" - subu t7,a2,96 # subtract "pref 30 unsafe" region - # below we have at least 1 64-byte chunk which is "pref 30 safe" - andi t6,t7,0x3f # t6 is past "64-byte safe chunks" reminder - subu t5,t7,t6 # subtract from t7 the reminder - # Here t5 counts bytes in 16w "safe" chunks - addu t4,a0,t5 # Now t4 is the dst after 64-byte "safe" chunks - -# Don't use "pref 30,0(a0)" for a0 in a "middle" of a cache line -# pref 30,0(a0) -# Here we are in the region, where it is safe to use "pref 30,64(a0)" -.Lloop16w: - addiu a0,a0,64 - pref 30,-32(a0) # continue setting up the dest, addr 64-32 - sw a1,-64(a0) - sw a1,-60(a0) - sw a1,-56(a0) - sw a1,-52(a0) - sw a1,-48(a0) - sw a1,-44(a0) - sw a1,-40(a0) - sw a1,-36(a0) - nop - nop # the extra nop instructions help to balance - nop # cycles needed for "store" + "fill" + "evict" - nop # For 64byte store there are needed 8 fill - nop # and 8 evict cycles, i.e. at least 32 instr. - nop - nop - pref 30,0(a0) # continue setting up the dest, addr 64-0 - sw a1,-32(a0) - sw a1,-28(a0) - sw a1,-24(a0) - sw a1,-20(a0) - sw a1,-16(a0) - sw a1,-12(a0) - sw a1,-8(a0) - sw a1,-4(a0) - nop - nop - nop - nop # NOTE: adding 14 nop-s instead of 12 nop-s - nop # gives better results for "fast" memory - nop - bne a0,t4,.Lloop16w - nop - - beq a0,a3,.Lchk8w # maybe no more 64-byte chunks? - nop # this "delayed slot" is useless ... - -.Lloop16w_nopref30: # there could be up to 3 "64-byte nopref30" chunks - addiu a0,a0,64 - sw a1,-64(a0) - sw a1,-60(a0) - sw a1,-56(a0) - sw a1,-52(a0) - sw a1,-48(a0) - sw a1,-44(a0) - sw a1,-40(a0) - sw a1,-36(a0) - sw a1,-32(a0) - sw a1,-28(a0) - sw a1,-24(a0) - sw a1,-20(a0) - sw a1,-16(a0) - sw a1,-12(a0) - sw a1,-8(a0) - bne a0,a3,.Lloop16w_nopref30 - sw a1,-4(a0) - -.Lchk8w: # t8 here is the byte count past 64-byte chunks - - andi t7,t8,0x1f # is there a 32-byte chunk? - # the t7 is the reminder count past 32-bytes - beq t8,t7,.Lchk1w # when t8==t7, no 32-byte chunk - move a2,t7 - - sw a1,0(a0) - sw a1,4(a0) - sw a1,8(a0) - sw a1,12(a0) - sw a1,16(a0) - sw a1,20(a0) - sw a1,24(a0) - sw a1,28(a0) - addiu a0,a0,32 - -.Lchk1w: - andi t8,a2,0x3 # now t8 is the reminder past 1w chunks - beq a2,t8,.Llast4 - subu a3,a2,t8 # a3 is the count of bytes in 1w chunks - addu a3,a0,a3 # now a3 is the dst address past the 1w chunks - -# copying in words (4-byte chunks) -.LwordCopy_loop: - addiu a0,a0,4 - bne a0,a3,.LwordCopy_loop - sw a1,-4(a0) - -.Llast4:beq a0,t0,.Llast4e -.Llast4l:addiu a0,a0,1 - bne a0,t0,.Llast4l - sb a1,-1(a0) - -.Llast4e: - j ra - nop - - .set at - .set reorder - -END(memset_cmips) - - -/************************************************************************ - * Implementation : Static functions - ************************************************************************/ - diff --git a/libcutils/tests/memset_mips/memset_omips.S b/libcutils/tests/memset_mips/memset_omips.S deleted file mode 100644 index 4c47001..0000000 --- a/libcutils/tests/memset_mips/memset_omips.S +++ /dev/null @@ -1,90 +0,0 @@ -/* Copyright (C) 2002, 2003 Free Software Foundation, Inc. - This file is part of the GNU C Library. - Contributed by Hartvig Ekner <hartvige@mips.com>, 2002. - - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - The GNU C Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with the GNU C Library; if not, write to the Free - Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307 USA. */ - -/* void *memset_omips(void *s, int c, size_t n). */ - -#include "machine/asm.h" - -#ifdef __mips64 -#error mips32 code being compiled for mips64! -#endif - -#if defined(__MIPSEB__) -#error big-endian is not supported in Broadcom MIPS Android platform -# define SWHI swl /* high part is left in big-endian */ -#else -# define SWHI swr /* high part is right in little-endian */ -#endif - -LEAF (memset_omips,0) - .set noreorder - - slti t1, a2, 8 # Less than 8? - bne t1, zero, .Llast8 - move v0, a0 # Setup exit value before too late - - beq a1, zero, .Lueven # If zero pattern, no need to extend - andi a1, 0xff # Avoid problems with bogus arguments - sll t0, a1, 8 - or a1, t0 - sll t0, a1, 16 - or a1, t0 # a1 is now pattern in full word - -.Lueven: - subu t0, zero, a0 # Unaligned address? - andi t0, 0x3 - beq t0, zero, .Lchkw - subu a2, t0 - SWHI a1, 0(a0) # Yes, handle first unaligned part - addu a0, t0 # Now both a0 and a2 are updated - -.Lchkw: - andi t0, a2, 0x7 # Enough left for one loop iteration? - beq t0, a2, .Lchkl - subu a3, a2, t0 - addu a3, a0 # a3 is last loop address +1 - move a2, t0 # a2 is now # of bytes left after loop -.Lloopw: - addiu a0, 8 # Handle 2 words pr. iteration - sw a1, -8(a0) - bne a0, a3, .Lloopw - sw a1, -4(a0) - -.Lchkl: - andi t0, a2, 0x4 # Check if there is at least a full - beq t0, zero, .Llast8 # word remaining after the loop - subu a2, t0 - sw a1, 0(a0) # Yes... - addiu a0, 4 - -.Llast8: - blez a2, .Lexit # Handle last 8 bytes (if cnt>0) - addu a3, a2, a0 # a3 is last address +1 -.Llst8l: - addiu a0, 1 - bne a0, a3, .Llst8l - sb a1, -1(a0) -.Lexit: - j ra # Bye, bye - nop - - .set reorder -END (memset_omips) - - diff --git a/libcutils/tests/memset_mips/test_memset.c b/libcutils/tests/memset_mips/test_memset.c deleted file mode 100644 index 9705c65..0000000 --- a/libcutils/tests/memset_mips/test_memset.c +++ /dev/null @@ -1,235 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <assert.h> -#include <cutils/memory.h> -#include <time.h> - -/* - * All systems must implement or emulate the rdhwr instruction to read - * the userlocal register. Systems that emulate also return teh count register - * when accessing register $2 so this should work on most systems - */ -#define USE_RDHWR - -#ifdef USE_RDHWR -#define UNITS "cycles" -#define SCALE 2 /* Most CPU's */ -static inline uint32_t -get_count(void) -{ - uint32_t res; - asm volatile (".set push; .set mips32r2; rdhwr %[res],$2; .set pop" : [res] "=r" (res) : : "memory"); - return res; -} -#else -#define UNITS "ns" -#define SCALE 1 -static inline uint32_t -get_count(void) -{ - struct timespec now; - uint32_t res; - clock_gettime(CLOCK_REALTIME, &now); - res = (uint32_t)(now.tv_sec * 1000000000LL + now.tv_nsec); - // printf ("now=%d.%09d res=%d\n", (int)now.tv_sec, (int)now.tv_nsec, res); - return res; -} -#endif - -uint32_t overhead; -void -measure_overhead(void) -{ - int i; - uint32_t start, stop, delta; - for (i = 0; i < 32; i++) { - start = get_count(); - stop = get_count(); - delta = stop - start; - if (overhead == 0 || delta < overhead) - overhead = delta; - } - printf("overhead is %d"UNITS"\n", overhead); -} - -uint32_t -timeone(void (*fn)(), void *d, uint32_t val, uint32_t bytes) -{ - uint32_t start, stop, delta; - start = get_count(); - (*fn)(d, val, bytes); - stop = get_count(); - delta = stop - start - overhead; - // printf ("start=0x%08x stop=0x%08x delta=0x%08x\n", start, stop, delta); - return delta * SCALE; -} - -/* define VERIFY to check that memset only touches the bytes it's supposed to */ -/*#define VERIFY*/ - -/* - * Using a big arena means that memset will most likely miss in the cache - * NB Enabling verification effectively warms up the cache... - */ -#define ARENASIZE 0x1000000 -#ifdef VERIFY -char arena[ARENASIZE+8]; /* Allow space for guard words */ -#else -char arena[ARENASIZE]; -#endif - -void -testone(char *tag, void (*fn)(), int trials, int minbytes, int maxbytes, int size, int threshold) -{ - int offset; - void *d; - void *p; - uint32_t v, notv = 0; - uint32_t n; - int i, units; - int totalunits = 0, totalbytes = 0, samples = 0; - - /* Reset RNG to ensure each test uses same random values */ - srand(0); /* FIXME should be able to use some other seed than 0 */ - - for (i = 0; i < trials; i++) { - n = minbytes + (rand() % (maxbytes-minbytes)); /* How many bytes to do */ - offset = ((rand() % (ARENASIZE-n))); /* Where to start */ - -#ifdef VERIFY - offset += 4; /* Allow space for guard word at beginning */ -#endif - v = rand(); - - /* Adjust alignment and sizes based on transfer size */ - switch (size) { - case 1: - v &= 0xff; - notv = ~v & 0xff; - break; - case 2: - v &= 0xffff; - notv = ~v & 0xffff; - offset &= ~1; - n &= ~1; - break; - case 4: - notv = ~v; - offset &= ~3; - n &= ~3; - break; - } - - d = &arena[offset]; - -#ifdef VERIFY - /* Initialise the area and guard words */ - for (p = &arena[offset-4]; p < (void *)&arena[offset+n+4]; p = (void *)((uint32_t)p + size)) { - if (size == 1) - *(uint8_t *)p = notv; - else if (size == 2) - *(uint16_t *)p = notv; - else if (size == 4) - *(uint32_t *)p = notv; - } -#endif - units = timeone(fn, d, v, n); -#ifdef VERIFY - /* Check the area and guard words */ - for (p = &arena[offset-4]; p < (void *)&arena[offset+n+4]; p = (void *)((uint32_t)p + size)) { - uint32_t got = 0; - if (size == 1) - got = *(uint8_t *)p; - else if (size == 2) - got = *(uint16_t *)p; - else if (size == 4) - got = *(uint32_t *)p; - if (p < (void *)&arena[offset]) { - if (got != notv) - printf ("%s: verify failure: preguard:%p d=%p v=%08x got=%08x n=%d\n", tag, p, d, v, got, n); - } - else if (p < (void *)&arena[offset+n]) { - if (got != v) - printf ("%s: verify failure: arena:%p d=%p v=%08x got=%08x n=%d\n", tag, p, d, v, n); - } - else { - if (got != notv) - printf ("%s: verify failure: postguard:%p d=%p v=%08x got=%08x n=%d\n", tag, p, d, v, n); - } - } -#endif - - /* If the cycle count looks reasonable include it in the statistics */ - if (units < threshold) { - totalbytes += n; - totalunits += units; - samples++; - } - } - - printf("%s: samples=%d avglen=%d avg" UNITS "=%d bp"UNITS"=%g\n", - tag, samples, totalbytes/samples, totalunits/samples, (double)totalbytes/(double)totalunits); -} - -extern void android_memset32_dumb(uint32_t* dst, uint32_t value, size_t size); -extern void android_memset16_dumb(uint32_t* dst, uint16_t value, size_t size); -extern void android_memset32_test(uint32_t* dst, uint32_t value, size_t size); -extern void android_memset16_test(uint32_t* dst, uint16_t value, size_t size); -extern void memset_cmips(void* dst, int value, size_t size); -extern void memset_omips(void* dst, int value, size_t size); - -int -main(int argc, char **argv) -{ - int i; - struct { - char *type; - int trials; - int minbytes, maxbytes; - } *pp, params[] = { - {"small", 10000, 0, 64}, - {"medium", 10000, 64, 512}, - {"large", 10000, 512, 1280}, - {"varied", 10000, 0, 1280}, - }; -#define NPARAMS (sizeof(params)/sizeof(params[0])) - struct { - char *name; - void (*fn)(); - int size; - } *fp, functions[] = { - {"dmemset16", (void (*)())android_memset16_dumb, 2}, - {"tmemset16", (void (*)())android_memset16_test, 2}, - {"lmemset16", (void (*)())android_memset16, 2}, - - {"dmemset32", (void (*)())android_memset32_dumb, 4}, - {"tmemset32", (void (*)())android_memset32_test, 4}, - {"lmemset32", (void (*)())android_memset32, 4}, - - {"cmemset", (void (*)())memset_cmips, 1}, - {"omemset", (void (*)())memset_omips, 1}, - {"lmemset", (void (*)())memset, 1}, - }; -#define NFUNCTIONS (sizeof(functions)/sizeof(functions[0])) - char tag[40]; - int threshold; - - measure_overhead(); - - /* Warm up the page cache */ - memset(arena, 0xff, ARENASIZE); /* use 0xff now to avoid COW later */ - - for (fp = functions; fp < &functions[NFUNCTIONS]; fp++) { - (fp->fn)(arena, 0xffffffff, ARENASIZE); /* one call to get the code into Icache */ - for (pp = params; pp < ¶ms[NPARAMS]; pp++) { - sprintf(tag, "%10s: %7s %4d-%4d", fp->name, pp->type, pp->minbytes, pp->maxbytes); - - /* Set the cycle threshold */ - threshold = pp->maxbytes * 4 * 10; /* reasonable for cycles and ns */ - testone(tag, fp->fn, pp->trials, pp->minbytes, pp->maxbytes, fp->size, threshold); - } - printf ("\n"); - } - - return 0; -} diff --git a/libcutils/threads.c b/libcutils/threads.c index 42cc928..bf182f0 100644 --- a/libcutils/threads.c +++ b/libcutils/threads.c @@ -1,5 +1,4 @@ -/* libs/cutils/threads.c -** +/* ** Copyright (C) 2007, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,13 +13,12 @@ ** See the License for the specific language governing permissions and ** limitations under the License. */ + #include <cutils/threads.h> #ifdef HAVE_PTHREADS void* thread_store_get( thread_store_t* store ) { - const pthread_key_t k = store->tls; - if (!store->has_tls) return NULL; diff --git a/libcutils/trace.c b/libcutils/trace.c index 9754a44..f57aac2 100644 --- a/libcutils/trace.c +++ b/libcutils/trace.c @@ -28,7 +28,7 @@ #include <cutils/trace.h> #define LOG_TAG "cutils-trace" -#include <cutils/log.h> +#include <log/log.h> volatile int32_t atrace_is_ready = 0; int atrace_marker_fd = -1; @@ -86,7 +86,6 @@ static bool atrace_is_cmdline_match(const char* cmdline) static bool atrace_is_app_tracing_enabled() { bool sys_debuggable = false; - bool proc_debuggable = false; char value[PROPERTY_VALUE_MAX]; bool result = false; diff --git a/libdiskconfig/Android.mk b/libdiskconfig/Android.mk index b5d83fa..624e385 100644 --- a/libdiskconfig/Android.mk +++ b/libdiskconfig/Android.mk @@ -12,6 +12,7 @@ LOCAL_SRC_FILES := $(commonSources) LOCAL_MODULE := libdiskconfig LOCAL_MODULE_TAGS := optional LOCAL_SYSTEM_SHARED_LIBRARIES := libcutils liblog libc +LOCAL_CFLAGS := -Werror include $(BUILD_SHARED_LIBRARY) ifeq ($(HOST_OS),linux) diff --git a/libdiskconfig/diskconfig.c b/libdiskconfig/diskconfig.c index 6fd81b7..1167d4b 100644 --- a/libdiskconfig/diskconfig.c +++ b/libdiskconfig/diskconfig.c @@ -30,7 +30,7 @@ #include <linux/fs.h> #include <cutils/config_utils.h> -#include <cutils/log.h> +#include <log/log.h> #include <diskconfig/diskconfig.h> @@ -337,7 +337,7 @@ validate(struct disk_info *dinfo) } #if 1 - ALOGV("Device/file %s: size=%llu bytes, num_lba=%u, sect_size=%d", + ALOGV("Device/file %s: size=%" PRIu64 " bytes, num_lba=%u, sect_size=%d", dinfo->device, disk_size, dinfo->num_lba, dinfo->sect_size); #endif diff --git a/libdiskconfig/diskutils.c b/libdiskconfig/diskutils.c index e325735..5d0ee62 100644 --- a/libdiskconfig/diskutils.c +++ b/libdiskconfig/diskutils.c @@ -19,13 +19,14 @@ #include <errno.h> #include <fcntl.h> +#include <inttypes.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/stat.h> -#include <cutils/log.h> +#include <log/log.h> #include <diskconfig/diskconfig.h> @@ -35,12 +36,12 @@ write_raw_image(const char *dst, const char *src, loff_t offset, int test) int dst_fd = -1; int src_fd = -1; uint8_t buffer[2048]; - int nr_bytes; - int tmp; + ssize_t nr_bytes; + ssize_t tmp; int done = 0; uint64_t total = 0; - ALOGI("Writing RAW image '%s' to '%s' (offset=%llu)", src, dst, offset); + ALOGI("Writing RAW image '%s' to '%s' (offset=%llu)", src, dst, (unsigned long long)offset); if ((src_fd = open(src, O_RDONLY)) < 0) { ALOGE("Could not open %s for reading (errno=%d).", src, errno); goto fail; @@ -53,7 +54,7 @@ write_raw_image(const char *dst, const char *src, loff_t offset, int test) } if (lseek64(dst_fd, offset, SEEK_SET) != offset) { - ALOGE("Could not seek to offset %lld in %s.", offset, dst); + ALOGE("Could not seek to offset %lld in %s.", (long long)offset, dst); goto fail; } } @@ -101,7 +102,7 @@ write_raw_image(const char *dst, const char *src, loff_t offset, int test) if (dst_fd >= 0) fsync(dst_fd); - ALOGI("Wrote %llu bytes to %s @ %lld", total, dst, offset); + ALOGI("Wrote %" PRIu64 " bytes to %s @ %lld", total, dst, (long long)offset); close(src_fd); if (dst_fd >= 0) diff --git a/libdiskconfig/write_lst.c b/libdiskconfig/write_lst.c index 826ef7a..90b1c82 100644 --- a/libdiskconfig/write_lst.c +++ b/libdiskconfig/write_lst.c @@ -71,18 +71,18 @@ wlist_commit(int fd, struct write_list *lst, int test) { for(; lst; lst = lst->next) { if (lseek64(fd, lst->offset, SEEK_SET) != (loff_t)lst->offset) { - ALOGE("Cannot seek to the specified position (%lld).", lst->offset); + ALOGE("Cannot seek to the specified position (%lld).", (long long)lst->offset); goto fail; } if (!test) { if (write(fd, lst->data, lst->len) != (int)lst->len) { ALOGE("Failed writing %u bytes at position %lld.", lst->len, - lst->offset); + (long long)lst->offset); goto fail; } } else - ALOGI("Would write %d bytes @ offset %lld.", lst->len, lst->offset); + ALOGI("Would write %d bytes @ offset %lld.", lst->len, (long long)lst->offset); } return 0; diff --git a/libion/Android.mk b/libion/Android.mk index e5d495b..6562cd3 100644 --- a/libion/Android.mk +++ b/libion/Android.mk @@ -1,4 +1,4 @@ -LOCAL_PATH:= $(call my-dir) +LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES := ion.c @@ -7,6 +7,7 @@ LOCAL_MODULE_TAGS := optional LOCAL_SHARED_LIBRARIES := liblog LOCAL_C_INCLUDES := $(LOCAL_PATH)/include $(LOCAL_PATH)/kernel-headers LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include $(LOCAL_PATH)/kernel-headers +LOCAL_CFLAGS := -Werror include $(BUILD_SHARED_LIBRARY) include $(CLEAR_VARS) @@ -15,6 +16,7 @@ LOCAL_MODULE := iontest LOCAL_MODULE_TAGS := optional tests LOCAL_C_INCLUDES := $(LOCAL_PATH)/include $(LOCAL_PATH)/kernel-headers LOCAL_SHARED_LIBRARIES := liblog +LOCAL_CFLAGS := -Werror include $(BUILD_EXECUTABLE) include $(call first-makefiles-under,$(LOCAL_PATH)) diff --git a/liblog/Android.mk b/liblog/Android.mk index 0d6c970..a7eead9 100644 --- a/liblog/Android.mk +++ b/liblog/Android.mk @@ -16,7 +16,11 @@ LOCAL_PATH := $(my-dir) include $(CLEAR_VARS) +ifneq ($(TARGET_USES_LOGD),false) liblog_sources := logd_write.c +else +liblog_sources := logd_write_kern.c +endif # some files must not be compiled when building against Mingw # they correspond to features not used by our host development tools @@ -42,19 +46,28 @@ else endif liblog_host_sources := $(liblog_sources) fake_log_device.c -liblog_target_sources = $(liblog_sources) log_read.c +liblog_target_sources := $(liblog_sources) log_time.cpp +ifneq ($(TARGET_USES_LOGD),false) +liblog_target_sources += log_read.c +else +liblog_target_sources += log_read_kern.c +endif # Shared and static library for host # ======================================================== LOCAL_MODULE := liblog LOCAL_SRC_FILES := $(liblog_host_sources) -LOCAL_LDLIBS := -lpthread -LOCAL_CFLAGS := -DFAKE_LOG_DEVICE=1 +LOCAL_CFLAGS := -DFAKE_LOG_DEVICE=1 -Werror +LOCAL_MULTILIB := both include $(BUILD_HOST_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := liblog LOCAL_WHOLE_STATIC_LIBRARIES := liblog +ifeq ($(strip $(HOST_OS)),linux) +LOCAL_LDLIBS := -lrt +endif +LOCAL_MULTILIB := both include $(BUILD_HOST_SHARED_LIBRARY) @@ -63,8 +76,7 @@ include $(BUILD_HOST_SHARED_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := lib64log LOCAL_SRC_FILES := $(liblog_host_sources) -LOCAL_LDLIBS := -lpthread -LOCAL_CFLAGS := -DFAKE_LOG_DEVICE=1 -m64 +LOCAL_CFLAGS := -DFAKE_LOG_DEVICE=1 -m64 -Werror include $(BUILD_HOST_STATIC_LIBRARY) # Shared and static library for target @@ -72,11 +84,13 @@ include $(BUILD_HOST_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := liblog LOCAL_SRC_FILES := $(liblog_target_sources) +LOCAL_CFLAGS := -Werror include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := liblog LOCAL_WHOLE_STATIC_LIBRARIES := liblog +LOCAL_CFLAGS := -Werror include $(BUILD_SHARED_LIBRARY) include $(call first-makefiles-under,$(LOCAL_PATH)) diff --git a/liblog/event_tag_map.c b/liblog/event_tag_map.c index f3d1e2f..bea99aa 100644 --- a/liblog/event_tag_map.c +++ b/liblog/event_tag_map.c @@ -13,15 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include <log/event_tag_map.h> -#include <log/log.h> +#include <assert.h> +#include <errno.h> +#include <fcntl.h> #include <stdlib.h> #include <string.h> -#include <fcntl.h> #include <sys/mman.h> -#include <errno.h> -#include <assert.h> + +#include <log/event_tag_map.h> +#include <log/log.h> #define OUT_TAG "EventTagMap" @@ -52,7 +53,6 @@ static int countMapLines(const EventTagMap* map); static int parseMapLines(EventTagMap* map); static int scanTagLine(char** pData, EventTag* tag, int lineNum); static int sortTags(EventTagMap* map); -static void dumpTags(const EventTagMap* map); /* @@ -185,8 +185,6 @@ static inline int isCharDigit(char c) */ static int processFile(EventTagMap* map) { - EventTag* tagArray = NULL; - /* get a tag count */ map->numTags = countMapLines(map); if (map->numTags < 0) @@ -422,17 +420,3 @@ static int sortTags(EventTagMap* map) return 0; } - -/* - * Dump the tag array for debugging. - */ -static void dumpTags(const EventTagMap* map) -{ - int i; - - for (i = 0; i < map->numTags; i++) { - const EventTag* tag = &map->tagArray[i]; - printf(" %3d: %6d '%s'\n", i, tag->tagIndex, tag->tagStr); - } -} - diff --git a/liblog/fake_log_device.c b/liblog/fake_log_device.c index da83a85..136792d 100644 --- a/liblog/fake_log_device.c +++ b/liblog/fake_log_device.c @@ -21,18 +21,22 @@ */ #include "fake_log_device.h" -#include <log/logd.h> - -#include <stdlib.h> -#include <string.h> #include <ctype.h> #include <errno.h> #include <fcntl.h> +#include <stdlib.h> +#include <string.h> + +#include <log/logd.h> #ifdef HAVE_PTHREADS #include <pthread.h> #endif +#ifndef __unused +#define __unused __attribute__((__unused__)) +#endif + #define kMaxTagLen 16 /* from the long-dead utils/Log.cpp */ #define kTagSetSize 16 /* arbitrary */ @@ -613,7 +617,7 @@ static int logClose(int fd) /* * Open a log output device and return a fake fd. */ -static int logOpen(const char* pathName, int flags) +static int logOpen(const char* pathName, int flags __unused) { LogState *logState; int fd = -1; diff --git a/liblog/log_read.c b/liblog/log_read.c index 47aa711..ca5a1a7 100644 --- a/liblog/log_read.c +++ b/liblog/log_read.c @@ -14,37 +14,180 @@ ** limitations under the License. */ -#define _GNU_SOURCE /* asprintf for x86 host */ #include <errno.h> #include <fcntl.h> +#include <inttypes.h> #include <poll.h> -#include <string.h> -#include <stdio.h> +#include <signal.h> +#include <stddef.h> +#define NOMINMAX /* for windows to suppress definition of min in stdlib.h */ #include <stdlib.h> +#include <string.h> +#include <sys/cdefs.h> +#include <unistd.h> + #include <cutils/list.h> +#include <cutils/sockets.h> #include <log/log.h> #include <log/logger.h> -#include <sys/ioctl.h> +/* branchless on many architectures. */ +#define min(x,y) ((y) ^ (((x) ^ (y)) & -((x) < (y)))) + +#define WEAK __attribute__((weak)) +#ifndef __unused +#define __unused __attribute__((unused)) +#endif + +/* Private copy of ../libcutils/socket_local_client.c prevent library loops */ + +#ifdef HAVE_WINSOCK + +int WEAK socket_local_client(const char *name, int namespaceId, int type) +{ + errno = ENOSYS; + return -ENOSYS; +} + +#else /* !HAVE_WINSOCK */ + +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/select.h> +#include <sys/types.h> + +/* Private copy of ../libcutils/socket_local.h prevent library loops */ +#define FILESYSTEM_SOCKET_PREFIX "/tmp/" +#define ANDROID_RESERVED_SOCKET_PREFIX "/dev/socket/" +/* End of ../libcutils/socket_local.h */ + +#define LISTEN_BACKLOG 4 + +/* Documented in header file. */ +int WEAK socket_make_sockaddr_un(const char *name, int namespaceId, + struct sockaddr_un *p_addr, socklen_t *alen) +{ + memset (p_addr, 0, sizeof (*p_addr)); + size_t namelen; + + switch (namespaceId) { + case ANDROID_SOCKET_NAMESPACE_ABSTRACT: +#ifdef HAVE_LINUX_LOCAL_SOCKET_NAMESPACE + namelen = strlen(name); + + /* Test with length +1 for the *initial* '\0'. */ + if ((namelen + 1) > sizeof(p_addr->sun_path)) { + goto error; + } + + /* + * Note: The path in this case is *not* supposed to be + * '\0'-terminated. ("man 7 unix" for the gory details.) + */ + + p_addr->sun_path[0] = 0; + memcpy(p_addr->sun_path + 1, name, namelen); +#else /*HAVE_LINUX_LOCAL_SOCKET_NAMESPACE*/ + /* this OS doesn't have the Linux abstract namespace */ + + namelen = strlen(name) + strlen(FILESYSTEM_SOCKET_PREFIX); + /* unix_path_max appears to be missing on linux */ + if (namelen > sizeof(*p_addr) + - offsetof(struct sockaddr_un, sun_path) - 1) { + goto error; + } + + strcpy(p_addr->sun_path, FILESYSTEM_SOCKET_PREFIX); + strcat(p_addr->sun_path, name); +#endif /*HAVE_LINUX_LOCAL_SOCKET_NAMESPACE*/ + break; + + case ANDROID_SOCKET_NAMESPACE_RESERVED: + namelen = strlen(name) + strlen(ANDROID_RESERVED_SOCKET_PREFIX); + /* unix_path_max appears to be missing on linux */ + if (namelen > sizeof(*p_addr) + - offsetof(struct sockaddr_un, sun_path) - 1) { + goto error; + } + + strcpy(p_addr->sun_path, ANDROID_RESERVED_SOCKET_PREFIX); + strcat(p_addr->sun_path, name); + break; + + case ANDROID_SOCKET_NAMESPACE_FILESYSTEM: + namelen = strlen(name); + /* unix_path_max appears to be missing on linux */ + if (namelen > sizeof(*p_addr) + - offsetof(struct sockaddr_un, sun_path) - 1) { + goto error; + } + + strcpy(p_addr->sun_path, name); + break; + + default: + /* invalid namespace id */ + return -1; + } + + p_addr->sun_family = AF_LOCAL; + *alen = namelen + offsetof(struct sockaddr_un, sun_path) + 1; + return 0; +error: + return -1; +} + +/** + * connect to peer named "name" on fd + * returns same fd or -1 on error. + * fd is not closed on error. that's your job. + * + * Used by AndroidSocketImpl + */ +int WEAK socket_local_client_connect(int fd, const char *name, int namespaceId, + int type __unused) +{ + struct sockaddr_un addr; + socklen_t alen; + int err; + + err = socket_make_sockaddr_un(name, namespaceId, &addr, &alen); + + if (err < 0) { + goto error; + } + + if(connect(fd, (struct sockaddr *) &addr, alen) < 0) { + goto error; + } + + return fd; -#define __LOGGERIO 0xAE +error: + return -1; +} + +/** + * connect to peer named "name" + * returns fd or -1 on error + */ +int WEAK socket_local_client(const char *name, int namespaceId, int type) +{ + int s; -#define LOGGER_GET_LOG_BUF_SIZE _IO(__LOGGERIO, 1) /* size of log */ -#define LOGGER_GET_LOG_LEN _IO(__LOGGERIO, 2) /* used log len */ -#define LOGGER_GET_NEXT_ENTRY_LEN _IO(__LOGGERIO, 3) /* next entry len */ -#define LOGGER_FLUSH_LOG _IO(__LOGGERIO, 4) /* flush log */ -#define LOGGER_GET_VERSION _IO(__LOGGERIO, 5) /* abi version */ -#define LOGGER_SET_VERSION _IO(__LOGGERIO, 6) /* abi version */ + s = socket(AF_LOCAL, type, 0); + if(s < 0) return -1; -typedef char bool; -#define false (const bool)0 -#define true (const bool)1 + if ( 0 > socket_local_client_connect(s, name, namespaceId, type)) { + close(s); + return -1; + } -#define LOG_FILE_DIR "/dev/log/" + return s; +} -/* timeout in milliseconds */ -#define LOG_TIMEOUT_FLUSH 5 -#define LOG_TIMEOUT_NEVER -1 +#endif /* !HAVE_WINSOCK */ +/* End of ../libcutils/socket_local_client.c */ #define logger_for_each(logger, logger_list) \ for (logger = node_to_item((logger_list)->node.next, struct logger, node); \ @@ -56,47 +199,21 @@ static const char *LOG_NAME[LOG_ID_MAX] = { [LOG_ID_MAIN] = "main", [LOG_ID_RADIO] = "radio", [LOG_ID_EVENTS] = "events", - [LOG_ID_SYSTEM] = "system" + [LOG_ID_SYSTEM] = "system", + [LOG_ID_CRASH] = "crash", }; -const char *android_log_id_to_name(log_id_t log_id) { +const char *android_log_id_to_name(log_id_t log_id) +{ if (log_id >= LOG_ID_MAX) { log_id = LOG_ID_MAIN; } return LOG_NAME[log_id]; } -static int accessmode(int mode) +log_id_t android_name_to_log_id(const char *logName) { - if ((mode & O_ACCMODE) == O_WRONLY) { - return W_OK; - } - if ((mode & O_ACCMODE) == O_RDWR) { - return R_OK | W_OK; - } - return R_OK; -} - -/* repeated fragment */ -static int check_allocate_accessible(char **n, const char *b, int mode) -{ - *n = NULL; - - if (!b) { - return -EINVAL; - } - - asprintf(n, LOG_FILE_DIR "%s", b); - if (!*n) { - return -1; - } - - return access(*n, accessmode(mode)); -} - -log_id_t android_name_to_log_id(const char *logName) { const char *b; - char *n; int ret; if (!logName) { @@ -109,12 +226,6 @@ log_id_t android_name_to_log_id(const char *logName) { ++b; } - ret = check_allocate_accessible(&n, b, O_RDONLY); - free(n); - if (ret) { - return ret; - } - for(ret = LOG_ID_MIN; ret < LOG_ID_MAX; ++ret) { const char *l = LOG_NAME[ret]; if (l && !strcmp(b, l)) { @@ -128,27 +239,15 @@ struct logger_list { struct listnode node; int mode; unsigned int tail; + log_time start; pid_t pid; - unsigned int queued_lines; - int timeout_ms; - int error; - bool flush; - bool valid_entry; /* valiant(?) effort to deal with memory starvation */ - struct log_msg entry; -}; - -struct log_list { - struct listnode node; - struct log_msg entry; /* Truncated to event->len() + 1 to save space */ + int sock; }; struct logger { struct listnode node; struct logger_list *top; - int fd; log_id_t id; - short *revents; - struct listnode log_list; }; /* android_logger_alloc unimplemented, no use case */ @@ -159,91 +258,209 @@ static void android_logger_free(struct logger *logger) return; } - while (!list_empty(&logger->log_list)) { - struct log_list *entry = node_to_item( - list_head(&logger->log_list), struct log_list, node); - list_remove(&entry->node); - free(entry); - if (logger->top->queued_lines) { - logger->top->queued_lines--; - } - } - - if (logger->fd >= 0) { - close(logger->fd); - } - list_remove(&logger->node); free(logger); } +/* android_logger_alloc unimplemented, no use case */ + +/* method for getting the associated sublog id */ log_id_t android_logger_get_id(struct logger *logger) { return logger->id; } /* worker for sending the command to the logger */ -static int logger_ioctl(struct logger *logger, int cmd, int mode) +static ssize_t send_log_msg(struct logger *logger, + const char *msg, char *buf, size_t buf_size) { - char *n; - int f, ret; - - if (!logger || !logger->top) { - return -EFAULT; + ssize_t ret; + size_t len; + char *cp; + int errno_save = 0; + int sock = socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_RESERVED, + SOCK_STREAM); + if (sock < 0) { + return sock; } - if (((mode & O_ACCMODE) == O_RDWR) - || (((mode ^ logger->top->mode) & O_ACCMODE) == 0)) { - return ioctl(logger->fd, cmd); + if (msg) { + snprintf(buf, buf_size, msg, logger ? logger->id : (unsigned) -1); } - /* We go here if android_logger_list_open got mode wrong for this ioctl */ - ret = check_allocate_accessible(&n, android_log_id_to_name(logger->id), mode); - if (ret) { - free(n); - return ret; + len = strlen(buf) + 1; + ret = TEMP_FAILURE_RETRY(write(sock, buf, len)); + if (ret <= 0) { + goto done; } - f = open(n, mode); - free(n); - if (f < 0) { - return f; + len = buf_size; + cp = buf; + while ((ret = TEMP_FAILURE_RETRY(read(sock, cp, len))) > 0) { + struct pollfd p; + + if (((size_t)ret == len) || (buf_size < PAGE_SIZE)) { + break; + } + + len -= ret; + cp += ret; + + memset(&p, 0, sizeof(p)); + p.fd = sock; + p.events = POLLIN; + + /* Give other side 20ms to refill pipe */ + ret = TEMP_FAILURE_RETRY(poll(&p, 1, 20)); + + if (ret <= 0) { + break; + } + + if (!(p.revents & POLLIN)) { + ret = 0; + break; + } } - ret = ioctl(f, cmd); - close (f); + if (ret >= 0) { + ret += buf_size - len; + } +done: + if ((ret == -1) && errno) { + errno_save = errno; + } + close(sock); + if (errno_save) { + errno = errno_save; + } return ret; } +static int check_log_success(char *buf, ssize_t ret) +{ + if (ret < 0) { + return ret; + } + + if (strncmp(buf, "success", 7)) { + errno = EINVAL; + return -1; + } + + return 0; +} + int android_logger_clear(struct logger *logger) { - return logger_ioctl(logger, LOGGER_FLUSH_LOG, O_WRONLY); + char buf[512]; + + return check_log_success(buf, + send_log_msg(logger, "clear %d", buf, sizeof(buf))); } /* returns the total size of the log's ring buffer */ -int android_logger_get_log_size(struct logger *logger) +long android_logger_get_log_size(struct logger *logger) +{ + char buf[512]; + + ssize_t ret = send_log_msg(logger, "getLogSize %d", buf, sizeof(buf)); + if (ret < 0) { + return ret; + } + + if ((buf[0] < '0') || ('9' < buf[0])) { + return -1; + } + + return atol(buf); +} + +int android_logger_set_log_size(struct logger *logger, unsigned long size) { - return logger_ioctl(logger, LOGGER_GET_LOG_BUF_SIZE, O_RDWR); + char buf[512]; + + snprintf(buf, sizeof(buf), "setLogSize %d %lu", + logger ? logger->id : (unsigned) -1, size); + + return check_log_success(buf, send_log_msg(NULL, NULL, buf, sizeof(buf))); } /* * returns the readable size of the log's ring buffer (that is, amount of the * log consumed) */ -int android_logger_get_log_readable_size(struct logger *logger) +long android_logger_get_log_readable_size(struct logger *logger) { - return logger_ioctl(logger, LOGGER_GET_LOG_LEN, O_RDONLY); + char buf[512]; + + ssize_t ret = send_log_msg(logger, "getLogSizeUsed %d", buf, sizeof(buf)); + if (ret < 0) { + return ret; + } + + if ((buf[0] < '0') || ('9' < buf[0])) { + return -1; + } + + return atol(buf); } /* * returns the logger version */ -int android_logger_get_log_version(struct logger *logger) +int android_logger_get_log_version(struct logger *logger __unused) +{ + return 3; +} + +/* + * returns statistics + */ +ssize_t android_logger_get_statistics(struct logger_list *logger_list, + char *buf, size_t len) +{ + struct logger *logger; + char *cp = buf; + size_t remaining = len; + size_t n; + + n = snprintf(cp, remaining, "getStatistics"); + n = min(n, remaining); + remaining -= n; + cp += n; + + logger_for_each(logger, logger_list) { + n = snprintf(cp, remaining, " %d", logger->id); + n = min(n, remaining); + remaining -= n; + cp += n; + } + return send_log_msg(NULL, NULL, buf, len); +} + +ssize_t android_logger_get_prune_list(struct logger_list *logger_list __unused, + char *buf, size_t len) +{ + return send_log_msg(NULL, "getPruneList", buf, len); +} + +int android_logger_set_prune_list(struct logger_list *logger_list __unused, + char *buf, size_t len) { - int ret = logger_ioctl(logger, LOGGER_GET_VERSION, O_RDWR); - return (ret < 0) ? 1 : ret; + const char cmd[] = "setPruneList "; + const size_t cmdlen = sizeof(cmd) - 1; + + if (strlen(buf) > (len - cmdlen)) { + return -ENOMEM; /* KISS */ + } + memmove(buf + cmdlen, buf, len - cmdlen); + buf[len - 1] = '\0'; + memcpy(buf, cmd, cmdlen); + + return check_log_success(buf, send_log_msg(NULL, NULL, buf, len)); } struct logger_list *android_logger_list_alloc(int mode, @@ -256,10 +473,36 @@ struct logger_list *android_logger_list_alloc(int mode, if (!logger_list) { return NULL; } + list_init(&logger_list->node); logger_list->mode = mode; + logger_list->start.tv_sec = 0; + logger_list->start.tv_nsec = 0; logger_list->tail = tail; logger_list->pid = pid; + logger_list->sock = -1; + + return logger_list; +} + +struct logger_list *android_logger_list_alloc_time(int mode, + log_time start, + pid_t pid) +{ + struct logger_list *logger_list; + + logger_list = calloc(1, sizeof(*logger_list)); + if (!logger_list) { + return NULL; + } + + list_init(&logger_list->node); + logger_list->mode = mode; + logger_list->start = start; + logger_list->tail = 0; + logger_list->pid = pid; + logger_list->sock = -1; + return logger_list; } @@ -270,9 +513,7 @@ struct logger_list *android_logger_list_alloc(int mode, struct logger *android_logger_open(struct logger_list *logger_list, log_id_t id) { - struct listnode *node; struct logger *logger; - char *n; if (!logger_list || (id >= LOG_ID_MAX)) { goto err; @@ -289,28 +530,11 @@ struct logger *android_logger_open(struct logger_list *logger_list, goto err; } - if (check_allocate_accessible(&n, android_log_id_to_name(id), - logger_list->mode)) { - goto err_name; - } - - logger->fd = open(n, logger_list->mode); - if (logger->fd < 0) { - goto err_name; - } - - free(n); logger->id = id; - list_init(&logger->log_list); list_add_tail(&logger_list->node, &logger->node); logger->top = logger_list; - logger_list->timeout_ms = LOG_TIMEOUT_FLUSH; goto ok; -err_name: - free(n); -err_logger: - free(logger); err: logger = NULL; ok: @@ -336,315 +560,146 @@ struct logger_list *android_logger_list_open(log_id_t id, return logger_list; } -/* prevent memory starvation when backfilling */ -static unsigned int queue_threshold(struct logger_list *logger_list) -{ - return (logger_list->tail < 64) ? 64 : logger_list->tail; -} - -static bool low_queue(struct listnode *node) -{ - /* low is considered less than 2 */ - return list_head(node) == list_tail(node); -} - -/* Flush queues in sequential order, one at a time */ -static int android_logger_list_flush(struct logger_list *logger_list, - struct log_msg *log_msg) +static void caught_signal(int signum __unused) { - int ret = 0; - struct log_list *firstentry = NULL; - - while ((ret == 0) - && (logger_list->flush - || (logger_list->queued_lines > logger_list->tail))) { - struct logger *logger; - - /* Merge sort */ - bool at_least_one_is_low = false; - struct logger *firstlogger = NULL; - firstentry = NULL; - - logger_for_each(logger, logger_list) { - struct listnode *node; - struct log_list *oldest = NULL; - - /* kernel logger channels not necessarily time-sort order */ - list_for_each(node, &logger->log_list) { - struct log_list *entry = node_to_item(node, - struct log_list, node); - if (!oldest - || (entry->entry.entry.sec < oldest->entry.entry.sec) - || ((entry->entry.entry.sec == oldest->entry.entry.sec) - && (entry->entry.entry.nsec < oldest->entry.entry.nsec))) { - oldest = entry; - } - } - - if (!oldest) { - at_least_one_is_low = true; - continue; - } else if (low_queue(&logger->log_list)) { - at_least_one_is_low = true; - } - - if (!firstentry - || (oldest->entry.entry.sec < firstentry->entry.entry.sec) - || ((oldest->entry.entry.sec == firstentry->entry.entry.sec) - && (oldest->entry.entry.nsec < firstentry->entry.entry.nsec))) { - firstentry = oldest; - firstlogger = logger; - } - } - - if (!firstentry) { - break; - } - - /* when trimming list, tries to keep one entry behind in each bucket */ - if (!logger_list->flush - && at_least_one_is_low - && (logger_list->queued_lines < queue_threshold(logger_list))) { - break; - } - - /* within tail?, send! */ - if ((logger_list->tail == 0) - || (logger_list->queued_lines <= logger_list->tail)) { - ret = firstentry->entry.entry.hdr_size; - if (!ret) { - ret = sizeof(firstentry->entry.entry_v1); - } - ret += firstentry->entry.entry.len; - - memcpy(log_msg->buf, firstentry->entry.buf, ret + 1); - log_msg->extra.id = firstlogger->id; - } - - /* next entry */ - list_remove(&firstentry->node); - free(firstentry); - if (logger_list->queued_lines) { - logger_list->queued_lines--; - } - } - - /* Flushed the list, no longer in tail mode for continuing content */ - if (logger_list->flush && !firstentry) { - logger_list->tail = 0; - } - return ret; } /* Read from the selected logs */ int android_logger_list_read(struct logger_list *logger_list, struct log_msg *log_msg) { + int ret, e; struct logger *logger; - nfds_t nfds; - struct pollfd *p, *pollfds = NULL; - int error = 0, ret = 0; - - memset(log_msg, 0, sizeof(struct log_msg)); + struct sigaction ignore; + struct sigaction old_sigaction; + unsigned int old_alarm = 0; if (!logger_list) { - return -ENODEV; - } - - if (!(accessmode(logger_list->mode) & R_OK)) { - logger_list->error = EPERM; - goto done; - } - - nfds = 0; - logger_for_each(logger, logger_list) { - ++nfds; - } - if (nfds <= 0) { - error = ENODEV; - goto done; - } - - /* Do we have anything to offer from the buffer or state? */ - if (logger_list->valid_entry) { /* implies we are also in a flush state */ - goto flush; - } - - ret = android_logger_list_flush(logger_list, log_msg); - if (ret) { - goto done; + return -EINVAL; } - if (logger_list->error) { /* implies we are also in a flush state */ - goto done; + if (logger_list->mode & O_NONBLOCK) { + memset(&ignore, 0, sizeof(ignore)); + ignore.sa_handler = caught_signal; + sigemptyset(&ignore.sa_mask); } - /* Lets start grinding on metal */ - pollfds = calloc(nfds, sizeof(struct pollfd)); - if (!pollfds) { - error = ENOMEM; - goto flush; - } + if (logger_list->sock < 0) { + char buffer[256], *cp, c; - p = pollfds; - logger_for_each(logger, logger_list) { - p->fd = logger->fd; - p->events = POLLIN; - logger->revents = &p->revents; - ++p; - } - - while (!ret && !error) { - int result; - - /* If we oversleep it's ok, i.e. ignore EINTR. */ - result = TEMP_FAILURE_RETRY( - poll(pollfds, nfds, logger_list->timeout_ms)); - - if (result <= 0) { - if (result) { - error = errno; - } else if (logger_list->mode & O_NDELAY) { - error = EAGAIN; - } else { - logger_list->timeout_ms = LOG_TIMEOUT_NEVER; + int sock = socket_local_client("logdr", + ANDROID_SOCKET_NAMESPACE_RESERVED, + SOCK_SEQPACKET); + if (sock < 0) { + if ((sock == -1) && errno) { + return -errno; } - - logger_list->flush = true; - goto try_flush; + return sock; } - logger_list->timeout_ms = LOG_TIMEOUT_FLUSH; - - /* Anti starvation */ - if (!logger_list->flush - && (logger_list->queued_lines > (queue_threshold(logger_list) / 2))) { - /* Any queues with input pending that is low? */ - bool starving = false; - logger_for_each(logger, logger_list) { - if ((*(logger->revents) & POLLIN) - && low_queue(&logger->log_list)) { - starving = true; - break; - } - } + strcpy(buffer, + (logger_list->mode & O_NONBLOCK) ? "dumpAndClose" : "stream"); + cp = buffer + strlen(buffer); - /* pushback on any queues that are not low */ - if (starving) { - logger_for_each(logger, logger_list) { - if ((*(logger->revents) & POLLIN) - && !low_queue(&logger->log_list)) { - *(logger->revents) &= ~POLLIN; - } - } - } + strcpy(cp, " lids"); + cp += 5; + c = '='; + int remaining = sizeof(buffer) - (cp - buffer); + logger_for_each(logger, logger_list) { + ret = snprintf(cp, remaining, "%c%u", c, logger->id); + ret = min(ret, remaining); + remaining -= ret; + cp += ret; + c = ','; } - logger_for_each(logger, logger_list) { - unsigned int hdr_size; - struct log_list *entry; + if (logger_list->tail) { + ret = snprintf(cp, remaining, " tail=%u", logger_list->tail); + ret = min(ret, remaining); + remaining -= ret; + cp += ret; + } - if (!(*(logger->revents) & POLLIN)) { - continue; - } + if (logger_list->start.tv_sec || logger_list->start.tv_nsec) { + ret = snprintf(cp, remaining, " start=%" PRIu32 ".%09" PRIu32, + logger_list->start.tv_sec, + logger_list->start.tv_nsec); + ret = min(ret, remaining); + remaining -= ret; + cp += ret; + } - memset(logger_list->entry.buf, 0, sizeof(struct log_msg)); - /* NOTE: driver guarantees we read exactly one full entry */ - result = read(logger->fd, logger_list->entry.buf, - LOGGER_ENTRY_MAX_LEN); - if (result <= 0) { - if (!result) { - error = EIO; - } else if (errno != EINTR) { - error = errno; - } - continue; - } + if (logger_list->pid) { + ret = snprintf(cp, remaining, " pid=%u", logger_list->pid); + ret = min(ret, remaining); + remaining -= ret; + cp += ret; + } - if (logger_list->pid - && (logger_list->pid != logger_list->entry.entry.pid)) { - continue; + if (logger_list->mode & O_NONBLOCK) { + /* Deal with an unresponsive logd */ + sigaction(SIGALRM, &ignore, &old_sigaction); + old_alarm = alarm(30); + } + ret = write(sock, buffer, cp - buffer); + e = errno; + if (logger_list->mode & O_NONBLOCK) { + if (e == EINTR) { + e = ETIMEDOUT; } + alarm(old_alarm); + sigaction(SIGALRM, &old_sigaction, NULL); + } - hdr_size = logger_list->entry.entry.hdr_size; - if (!hdr_size) { - hdr_size = sizeof(logger_list->entry.entry_v1); + if (ret <= 0) { + close(sock); + if ((ret == -1) && e) { + return -e; } - - if ((hdr_size > sizeof(struct log_msg)) - || (logger_list->entry.entry.len - > sizeof(logger_list->entry.buf) - hdr_size) - || (logger_list->entry.entry.len != result - hdr_size)) { - error = EINVAL; - continue; + if (ret == 0) { + return -EIO; } + return ret; + } - logger_list->entry.extra.id = logger->id; - - /* speedup: If not tail, and only one list, send directly */ - if (!logger_list->tail - && (list_head(&logger_list->node) - == list_tail(&logger_list->node))) { - ret = result; - memcpy(log_msg->buf, logger_list->entry.buf, result + 1); - log_msg->extra.id = logger->id; - break; - } + logger_list->sock = sock; + } - entry = malloc(sizeof(*entry) - sizeof(entry->entry) + result + 1); + ret = 0; + while(1) { + memset(log_msg, 0, sizeof(*log_msg)); - if (!entry) { - logger_list->valid_entry = true; - error = ENOMEM; - break; + if (logger_list->mode & O_NONBLOCK) { + /* particularily useful if tombstone is reporting for logd */ + sigaction(SIGALRM, &ignore, &old_sigaction); + old_alarm = alarm(30); + } + /* NOTE: SOCK_SEQPACKET guarantees we read exactly one full entry */ + ret = recv(logger_list->sock, log_msg, LOGGER_ENTRY_MAX_LEN, 0); + e = errno; + if (logger_list->mode & O_NONBLOCK) { + if ((ret == 0) || (e == EINTR)) { + e = EAGAIN; + ret = -1; } - - logger_list->queued_lines++; - - memcpy(entry->entry.buf, logger_list->entry.buf, result); - entry->entry.buf[result] = '\0'; - list_add_tail(&logger->log_list, &entry->node); + alarm(old_alarm); + sigaction(SIGALRM, &old_sigaction, NULL); } if (ret <= 0) { -try_flush: - ret = android_logger_list_flush(logger_list, log_msg); - } - } - - free(pollfds); - -flush: - if (error) { - logger_list->flush = true; - } - - if (ret <= 0) { - ret = android_logger_list_flush(logger_list, log_msg); - - if (!ret && logger_list->valid_entry) { - ret = logger_list->entry.entry.hdr_size; - if (!ret) { - ret = sizeof(logger_list->entry.entry_v1); + if ((ret == -1) && e) { + return -e; } - ret += logger_list->entry.entry.len; - - memcpy(log_msg->buf, logger_list->entry.buf, - sizeof(struct log_msg)); - logger_list->valid_entry = false; + return ret; } - } -done: - if (logger_list->error) { - error = logger_list->error; - } - if (error) { - logger_list->error = error; - if (!ret) { - ret = -error; + logger_for_each(logger, logger_list) { + if (log_msg->entry.lid == logger->id) { + return ret; + } } } + /* NOTREACH */ return ret; } @@ -661,5 +716,9 @@ void android_logger_list_free(struct logger_list *logger_list) android_logger_free(logger); } + if (logger_list->sock >= 0) { + close (logger_list->sock); + } + free(logger_list); } diff --git a/liblog/log_read_kern.c b/liblog/log_read_kern.c new file mode 100644 index 0000000..41b8a51 --- /dev/null +++ b/liblog/log_read_kern.c @@ -0,0 +1,741 @@ +/* +** Copyright 2013-2014, 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. +*/ + +#define _GNU_SOURCE /* asprintf for x86 host */ +#include <errno.h> +#include <fcntl.h> +#include <poll.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/cdefs.h> +#include <sys/ioctl.h> + +#include <cutils/list.h> +#include <log/log.h> +#include <log/logger.h> + +#define __LOGGERIO 0xAE + +#define LOGGER_GET_LOG_BUF_SIZE _IO(__LOGGERIO, 1) /* size of log */ +#define LOGGER_GET_LOG_LEN _IO(__LOGGERIO, 2) /* used log len */ +#define LOGGER_GET_NEXT_ENTRY_LEN _IO(__LOGGERIO, 3) /* next entry len */ +#define LOGGER_FLUSH_LOG _IO(__LOGGERIO, 4) /* flush log */ +#define LOGGER_GET_VERSION _IO(__LOGGERIO, 5) /* abi version */ +#define LOGGER_SET_VERSION _IO(__LOGGERIO, 6) /* abi version */ + +typedef char bool; +#define false (const bool)0 +#define true (const bool)1 + +#define LOG_FILE_DIR "/dev/log/" + +/* timeout in milliseconds */ +#define LOG_TIMEOUT_FLUSH 5 +#define LOG_TIMEOUT_NEVER -1 + +#define logger_for_each(logger, logger_list) \ + for (logger = node_to_item((logger_list)->node.next, struct logger, node); \ + logger != node_to_item(&(logger_list)->node, struct logger, node); \ + logger = node_to_item((logger)->node.next, struct logger, node)) + +#ifndef __unused +#define __unused __attribute__((unused)) +#endif + +/* In the future, we would like to make this list extensible */ +static const char *LOG_NAME[LOG_ID_MAX] = { + [LOG_ID_MAIN] = "main", + [LOG_ID_RADIO] = "radio", + [LOG_ID_EVENTS] = "events", + [LOG_ID_SYSTEM] = "system", + [LOG_ID_CRASH] = "crash" +}; + +const char *android_log_id_to_name(log_id_t log_id) +{ + if (log_id >= LOG_ID_MAX) { + log_id = LOG_ID_MAIN; + } + return LOG_NAME[log_id]; +} + +static int accessmode(int mode) +{ + if ((mode & O_ACCMODE) == O_WRONLY) { + return W_OK; + } + if ((mode & O_ACCMODE) == O_RDWR) { + return R_OK | W_OK; + } + return R_OK; +} + +/* repeated fragment */ +static int check_allocate_accessible(char **n, const char *b, int mode) +{ + *n = NULL; + + if (!b) { + return -EINVAL; + } + + asprintf(n, LOG_FILE_DIR "%s", b); + if (!*n) { + return -1; + } + + return access(*n, accessmode(mode)); +} + +log_id_t android_name_to_log_id(const char *logName) +{ + const char *b; + char *n; + int ret; + + if (!logName) { + return -1; /* NB: log_id_t is unsigned */ + } + b = strrchr(logName, '/'); + if (!b) { + b = logName; + } else { + ++b; + } + + ret = check_allocate_accessible(&n, b, O_RDONLY); + free(n); + if (ret) { + return ret; + } + + for(ret = LOG_ID_MIN; ret < LOG_ID_MAX; ++ret) { + const char *l = LOG_NAME[ret]; + if (l && !strcmp(b, l)) { + return ret; + } + } + return -1; /* should never happen */ +} + +struct logger_list { + struct listnode node; + int mode; + unsigned int tail; + pid_t pid; + unsigned int queued_lines; + int timeout_ms; + int error; + bool flush; + bool valid_entry; /* valiant(?) effort to deal with memory starvation */ + struct log_msg entry; +}; + +struct log_list { + struct listnode node; + struct log_msg entry; /* Truncated to event->len() + 1 to save space */ +}; + +struct logger { + struct listnode node; + struct logger_list *top; + int fd; + log_id_t id; + short *revents; + struct listnode log_list; +}; + +/* android_logger_alloc unimplemented, no use case */ +/* android_logger_free not exported */ +static void android_logger_free(struct logger *logger) +{ + if (!logger) { + return; + } + + while (!list_empty(&logger->log_list)) { + struct log_list *entry = node_to_item( + list_head(&logger->log_list), struct log_list, node); + list_remove(&entry->node); + free(entry); + if (logger->top->queued_lines) { + logger->top->queued_lines--; + } + } + + if (logger->fd >= 0) { + close(logger->fd); + } + + list_remove(&logger->node); + + free(logger); +} + +log_id_t android_logger_get_id(struct logger *logger) +{ + return logger->id; +} + +/* worker for sending the command to the logger */ +static int logger_ioctl(struct logger *logger, int cmd, int mode) +{ + char *n; + int f, ret; + + if (!logger || !logger->top) { + return -EFAULT; + } + + if (((mode & O_ACCMODE) == O_RDWR) + || (((mode ^ logger->top->mode) & O_ACCMODE) == 0)) { + return ioctl(logger->fd, cmd); + } + + /* We go here if android_logger_list_open got mode wrong for this ioctl */ + ret = check_allocate_accessible(&n, android_log_id_to_name(logger->id), mode); + if (ret) { + free(n); + return ret; + } + + f = open(n, mode); + free(n); + if (f < 0) { + return f; + } + + ret = ioctl(f, cmd); + close (f); + + return ret; +} + +int android_logger_clear(struct logger *logger) +{ + return logger_ioctl(logger, LOGGER_FLUSH_LOG, O_WRONLY); +} + +/* returns the total size of the log's ring buffer */ +long android_logger_get_log_size(struct logger *logger) +{ + return logger_ioctl(logger, LOGGER_GET_LOG_BUF_SIZE, O_RDWR); +} + +int android_logger_set_log_size(struct logger *logger __unused, + unsigned long size __unused) +{ + return -ENOTSUP; +} + +/* + * returns the readable size of the log's ring buffer (that is, amount of the + * log consumed) + */ +long android_logger_get_log_readable_size(struct logger *logger) +{ + return logger_ioctl(logger, LOGGER_GET_LOG_LEN, O_RDONLY); +} + +/* + * returns the logger version + */ +int android_logger_get_log_version(struct logger *logger) +{ + int ret = logger_ioctl(logger, LOGGER_GET_VERSION, O_RDWR); + return (ret < 0) ? 1 : ret; +} + +/* + * returns statistics + */ +static const char unsupported[] = "18\nNot Supported\n\f"; + +ssize_t android_logger_get_statistics(struct logger_list *logger_list __unused, + char *buf, size_t len) +{ + strncpy(buf, unsupported, len); + return -ENOTSUP; +} + +ssize_t android_logger_get_prune_list(struct logger_list *logger_list __unused, + char *buf, size_t len) +{ + strncpy(buf, unsupported, len); + return -ENOTSUP; +} + +int android_logger_set_prune_list(struct logger_list *logger_list __unused, + char *buf, size_t len) +{ + static const char unsupported_error[] = "Unsupported"; + strncpy(buf, unsupported, len); + return -ENOTSUP; +} + +struct logger_list *android_logger_list_alloc(int mode, + unsigned int tail, + pid_t pid) +{ + struct logger_list *logger_list; + + logger_list = calloc(1, sizeof(*logger_list)); + if (!logger_list) { + return NULL; + } + list_init(&logger_list->node); + logger_list->mode = mode; + logger_list->tail = tail; + logger_list->pid = pid; + return logger_list; +} + +struct logger_list *android_logger_list_alloc_time(int mode, + log_time start __unused, + pid_t pid) +{ + return android_logger_list_alloc(mode, 0, pid); +} + +/* android_logger_list_register unimplemented, no use case */ +/* android_logger_list_unregister unimplemented, no use case */ + +/* Open the named log and add it to the logger list */ +struct logger *android_logger_open(struct logger_list *logger_list, + log_id_t id) +{ + struct listnode *node; + struct logger *logger; + char *n; + + if (!logger_list || (id >= LOG_ID_MAX)) { + goto err; + } + + logger_for_each(logger, logger_list) { + if (logger->id == id) { + goto ok; + } + } + + logger = calloc(1, sizeof(*logger)); + if (!logger) { + goto err; + } + + if (check_allocate_accessible(&n, android_log_id_to_name(id), + logger_list->mode)) { + goto err_name; + } + + logger->fd = open(n, logger_list->mode); + if (logger->fd < 0) { + goto err_name; + } + + free(n); + logger->id = id; + list_init(&logger->log_list); + list_add_tail(&logger_list->node, &logger->node); + logger->top = logger_list; + logger_list->timeout_ms = LOG_TIMEOUT_FLUSH; + goto ok; + +err_name: + free(n); +err_logger: + free(logger); +err: + logger = NULL; +ok: + return logger; +} + +/* Open the single named log and make it part of a new logger list */ +struct logger_list *android_logger_list_open(log_id_t id, + int mode, + unsigned int tail, + pid_t pid) +{ + struct logger_list *logger_list = android_logger_list_alloc(mode, tail, pid); + if (!logger_list) { + return NULL; + } + + if (!android_logger_open(logger_list, id)) { + android_logger_list_free(logger_list); + return NULL; + } + + return logger_list; +} + +/* prevent memory starvation when backfilling */ +static unsigned int queue_threshold(struct logger_list *logger_list) +{ + return (logger_list->tail < 64) ? 64 : logger_list->tail; +} + +static bool low_queue(struct listnode *node) +{ + /* low is considered less than 2 */ + return list_head(node) == list_tail(node); +} + +/* Flush queues in sequential order, one at a time */ +static int android_logger_list_flush(struct logger_list *logger_list, + struct log_msg *log_msg) +{ + int ret = 0; + struct log_list *firstentry = NULL; + + while ((ret == 0) + && (logger_list->flush + || (logger_list->queued_lines > logger_list->tail))) { + struct logger *logger; + + /* Merge sort */ + bool at_least_one_is_low = false; + struct logger *firstlogger = NULL; + firstentry = NULL; + + logger_for_each(logger, logger_list) { + struct listnode *node; + struct log_list *oldest = NULL; + + /* kernel logger channels not necessarily time-sort order */ + list_for_each(node, &logger->log_list) { + struct log_list *entry = node_to_item(node, + struct log_list, node); + if (!oldest + || (entry->entry.entry.sec < oldest->entry.entry.sec) + || ((entry->entry.entry.sec == oldest->entry.entry.sec) + && (entry->entry.entry.nsec < oldest->entry.entry.nsec))) { + oldest = entry; + } + } + + if (!oldest) { + at_least_one_is_low = true; + continue; + } else if (low_queue(&logger->log_list)) { + at_least_one_is_low = true; + } + + if (!firstentry + || (oldest->entry.entry.sec < firstentry->entry.entry.sec) + || ((oldest->entry.entry.sec == firstentry->entry.entry.sec) + && (oldest->entry.entry.nsec < firstentry->entry.entry.nsec))) { + firstentry = oldest; + firstlogger = logger; + } + } + + if (!firstentry) { + break; + } + + /* when trimming list, tries to keep one entry behind in each bucket */ + if (!logger_list->flush + && at_least_one_is_low + && (logger_list->queued_lines < queue_threshold(logger_list))) { + break; + } + + /* within tail?, send! */ + if ((logger_list->tail == 0) + || (logger_list->queued_lines <= logger_list->tail)) { + int diff; + ret = firstentry->entry.entry.hdr_size; + if (!ret) { + ret = sizeof(firstentry->entry.entry_v1); + } + + /* Promote entry to v3 format */ + memcpy(log_msg->buf, firstentry->entry.buf, ret); + diff = sizeof(firstentry->entry.entry_v3) - ret; + if (diff < 0) { + diff = 0; + } else if (diff > 0) { + memset(log_msg->buf + ret, 0, diff); + } + memcpy(log_msg->buf + ret + diff, firstentry->entry.buf + ret, + firstentry->entry.entry.len + 1); + ret += diff; + log_msg->entry.hdr_size = ret; + log_msg->entry.lid = firstlogger->id; + + ret += firstentry->entry.entry.len; + } + + /* next entry */ + list_remove(&firstentry->node); + free(firstentry); + if (logger_list->queued_lines) { + logger_list->queued_lines--; + } + } + + /* Flushed the list, no longer in tail mode for continuing content */ + if (logger_list->flush && !firstentry) { + logger_list->tail = 0; + } + return ret; +} + +/* Read from the selected logs */ +int android_logger_list_read(struct logger_list *logger_list, + struct log_msg *log_msg) +{ + struct logger *logger; + nfds_t nfds; + struct pollfd *p, *pollfds = NULL; + int error = 0, ret = 0; + + memset(log_msg, 0, sizeof(struct log_msg)); + + if (!logger_list) { + return -ENODEV; + } + + if (!(accessmode(logger_list->mode) & R_OK)) { + logger_list->error = EPERM; + goto done; + } + + nfds = 0; + logger_for_each(logger, logger_list) { + ++nfds; + } + if (nfds <= 0) { + error = ENODEV; + goto done; + } + + /* Do we have anything to offer from the buffer or state? */ + if (logger_list->valid_entry) { /* implies we are also in a flush state */ + goto flush; + } + + ret = android_logger_list_flush(logger_list, log_msg); + if (ret) { + goto done; + } + + if (logger_list->error) { /* implies we are also in a flush state */ + goto done; + } + + /* Lets start grinding on metal */ + pollfds = calloc(nfds, sizeof(struct pollfd)); + if (!pollfds) { + error = ENOMEM; + goto flush; + } + + p = pollfds; + logger_for_each(logger, logger_list) { + p->fd = logger->fd; + p->events = POLLIN; + logger->revents = &p->revents; + ++p; + } + + while (!ret && !error) { + int result; + + /* If we oversleep it's ok, i.e. ignore EINTR. */ + result = TEMP_FAILURE_RETRY( + poll(pollfds, nfds, logger_list->timeout_ms)); + + if (result <= 0) { + if (result) { + error = errno; + } else if (logger_list->mode & O_NDELAY) { + error = EAGAIN; + } else { + logger_list->timeout_ms = LOG_TIMEOUT_NEVER; + } + + logger_list->flush = true; + goto try_flush; + } + + logger_list->timeout_ms = LOG_TIMEOUT_FLUSH; + + /* Anti starvation */ + if (!logger_list->flush + && (logger_list->queued_lines > (queue_threshold(logger_list) / 2))) { + /* Any queues with input pending that is low? */ + bool starving = false; + logger_for_each(logger, logger_list) { + if ((*(logger->revents) & POLLIN) + && low_queue(&logger->log_list)) { + starving = true; + break; + } + } + + /* pushback on any queues that are not low */ + if (starving) { + logger_for_each(logger, logger_list) { + if ((*(logger->revents) & POLLIN) + && !low_queue(&logger->log_list)) { + *(logger->revents) &= ~POLLIN; + } + } + } + } + + logger_for_each(logger, logger_list) { + unsigned int hdr_size; + struct log_list *entry; + int diff; + + if (!(*(logger->revents) & POLLIN)) { + continue; + } + + memset(logger_list->entry.buf, 0, sizeof(struct log_msg)); + /* NOTE: driver guarantees we read exactly one full entry */ + result = read(logger->fd, logger_list->entry.buf, + LOGGER_ENTRY_MAX_LEN); + if (result <= 0) { + if (!result) { + error = EIO; + } else if (errno != EINTR) { + error = errno; + } + continue; + } + + if (logger_list->pid + && (logger_list->pid != logger_list->entry.entry.pid)) { + continue; + } + + hdr_size = logger_list->entry.entry.hdr_size; + if (!hdr_size) { + hdr_size = sizeof(logger_list->entry.entry_v1); + } + + if ((hdr_size > sizeof(struct log_msg)) + || (logger_list->entry.entry.len + > sizeof(logger_list->entry.buf) - hdr_size) + || (logger_list->entry.entry.len != result - hdr_size)) { + error = EINVAL; + continue; + } + + /* Promote entry to v3 format */ + diff = sizeof(logger_list->entry.entry_v3) - hdr_size; + if (diff > 0) { + if (logger_list->entry.entry.len + > sizeof(logger_list->entry.buf) - hdr_size - diff) { + error = EINVAL; + continue; + } + result += diff; + memmove(logger_list->entry.buf + hdr_size + diff, + logger_list->entry.buf + hdr_size, + logger_list->entry.entry.len + 1); + memset(logger_list->entry.buf + hdr_size, 0, diff); + logger_list->entry.entry.hdr_size = hdr_size + diff; + } + logger_list->entry.entry.lid = logger->id; + + /* speedup: If not tail, and only one list, send directly */ + if (!logger_list->tail + && (list_head(&logger_list->node) + == list_tail(&logger_list->node))) { + ret = result; + memcpy(log_msg->buf, logger_list->entry.buf, result + 1); + break; + } + + entry = malloc(sizeof(*entry) - sizeof(entry->entry) + result + 1); + + if (!entry) { + logger_list->valid_entry = true; + error = ENOMEM; + break; + } + + logger_list->queued_lines++; + + memcpy(entry->entry.buf, logger_list->entry.buf, result); + entry->entry.buf[result] = '\0'; + list_add_tail(&logger->log_list, &entry->node); + } + + if (ret <= 0) { +try_flush: + ret = android_logger_list_flush(logger_list, log_msg); + } + } + + free(pollfds); + +flush: + if (error) { + logger_list->flush = true; + } + + if (ret <= 0) { + ret = android_logger_list_flush(logger_list, log_msg); + + if (!ret && logger_list->valid_entry) { + ret = logger_list->entry.entry.hdr_size; + if (!ret) { + ret = sizeof(logger_list->entry.entry_v1); + } + ret += logger_list->entry.entry.len; + + memcpy(log_msg->buf, logger_list->entry.buf, + sizeof(struct log_msg)); + logger_list->valid_entry = false; + } + } + +done: + if (logger_list->error) { + error = logger_list->error; + } + if (error) { + logger_list->error = error; + if (!ret) { + ret = -error; + } + } + return ret; +} + +/* Close all the logs */ +void android_logger_list_free(struct logger_list *logger_list) +{ + if (logger_list == NULL) { + return; + } + + while (!list_empty(&logger_list->node)) { + struct listnode *node = list_head(&logger_list->node); + struct logger *logger = node_to_item(node, struct logger, node); + android_logger_free(logger); + } + + free(logger_list); +} diff --git a/liblog/log_time.cpp b/liblog/log_time.cpp new file mode 100644 index 0000000..755c2d9 --- /dev/null +++ b/liblog/log_time.cpp @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2014 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 <ctype.h> +#include <limits.h> +#include <stdio.h> +#include <string.h> +#include <sys/cdefs.h> + +#include <log/log_read.h> + +const char log_time::default_format[] = "%m-%d %H:%M:%S.%3q"; +const timespec log_time::EPOCH = { 0, 0 }; + +// Add %#q for fractional seconds to standard strptime function + +char *log_time::strptime(const char *s, const char *format) { + time_t now; +#ifdef __linux__ + *this = log_time(CLOCK_REALTIME); + now = tv_sec; +#else + time(&now); + tv_sec = now; + tv_nsec = 0; +#endif + + struct tm *ptm; +#if (defined(HAVE_LOCALTIME_R)) + struct tm tmBuf; + ptm = localtime_r(&now, &tmBuf); +#else + ptm = localtime(&now); +#endif + + char fmt[strlen(format) + 1]; + strcpy(fmt, format); + + char *ret = const_cast<char *> (s); + char *cp; + for (char *f = cp = fmt; ; ++cp) { + if (!*cp) { + if (f != cp) { + ret = ::strptime(ret, f, ptm); + } + break; + } + if (*cp != '%') { + continue; + } + char *e = cp; + ++e; +#if (defined(__BIONIC__)) + if (*e == 's') { + *cp = '\0'; + if (*f) { + ret = ::strptime(ret, f, ptm); + if (!ret) { + break; + } + } + tv_sec = 0; + while (isdigit(*ret)) { + tv_sec = tv_sec * 10 + *ret - '0'; + ++ret; + } + now = tv_sec; +#if (defined(HAVE_LOCALTIME_R)) + ptm = localtime_r(&now, &tmBuf); +#else + ptm = localtime(&now); +#endif + } else +#endif + { + unsigned num = 0; + while (isdigit(*e)) { + num = num * 10 + *e - '0'; + ++e; + } + if (*e != 'q') { + continue; + } + *cp = '\0'; + if (*f) { + ret = ::strptime(ret, f, ptm); + if (!ret) { + break; + } + } + unsigned long mul = NS_PER_SEC; + if (num == 0) { + num = INT_MAX; + } + tv_nsec = 0; + while (isdigit(*ret) && num && (mul > 1)) { + --num; + mul /= 10; + tv_nsec = tv_nsec + (*ret - '0') * mul; + ++ret; + } + } + f = cp = e; + ++f; + } + + if (ret) { + tv_sec = mktime(ptm); + return ret; + } + + // Upon error, place a known value into the class, the current time. +#ifdef __linux__ + *this = log_time(CLOCK_REALTIME); +#else + time(&now); + tv_sec = now; + tv_nsec = 0; +#endif + return ret; +} + +log_time log_time::operator-= (const timespec &T) { + // No concept of negative time, clamp to EPOCH + if (*this <= T) { + return *this = EPOCH; + } + + if (this->tv_nsec < (unsigned long int)T.tv_nsec) { + --this->tv_sec; + this->tv_nsec = NS_PER_SEC + this->tv_nsec - T.tv_nsec; + } else { + this->tv_nsec -= T.tv_nsec; + } + this->tv_sec -= T.tv_sec; + + return *this; +} + +log_time log_time::operator-= (const log_time &T) { + // No concept of negative time, clamp to EPOCH + if (*this <= T) { + return *this = EPOCH; + } + + if (this->tv_nsec < T.tv_nsec) { + --this->tv_sec; + this->tv_nsec = NS_PER_SEC + this->tv_nsec - T.tv_nsec; + } else { + this->tv_nsec -= T.tv_nsec; + } + this->tv_sec -= T.tv_sec; + + return *this; +} diff --git a/liblog/logd_write.c b/liblog/logd_write.c index 5766f8c..3171c78 100644 --- a/liblog/logd_write.c +++ b/liblog/logd_write.c @@ -13,41 +13,34 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include <time.h> -#include <stdio.h> +#include <errno.h> +#include <fcntl.h> #ifdef HAVE_PTHREADS #include <pthread.h> #endif -#include <unistd.h> -#include <errno.h> -#include <fcntl.h> -#include <string.h> -#include <stdlib.h> #include <stdarg.h> -#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> #include <sys/stat.h> +#include <sys/types.h> +#if (FAKE_LOG_DEVICE == 0) +#include <sys/socket.h> +#include <sys/un.h> +#endif +#include <time.h> +#include <unistd.h> -#include <log/logger.h> #include <log/logd.h> -#include <log/log.h> - -#define LOGGER_LOG_MAIN "log/main" -#define LOGGER_LOG_RADIO "log/radio" -#define LOGGER_LOG_EVENTS "log/events" -#define LOGGER_LOG_SYSTEM "log/system" +#include <log/logger.h> +#include <log/log_read.h> +#include <private/android_filesystem_config.h> #define LOG_BUF_SIZE 1024 #if FAKE_LOG_DEVICE -// This will be defined when building for the host. +/* This will be defined when building for the host. */ #include "fake_log_device.h" -#define log_open(pathname, flags) fakeLogOpen(pathname, flags) -#define log_writev(filedes, vector, count) fakeLogWritev(filedes, vector, count) -#define log_close(filedes) fakeLogClose(filedes) -#else -#define log_open(pathname, flags) open(pathname, (flags) | O_CLOEXEC) -#define log_writev(filedes, vector, count) writev(filedes, vector, count) -#define log_close(filedes) close(filedes) #endif static int __write_to_log_init(log_id_t, struct iovec *vec, size_t nr); @@ -56,13 +49,20 @@ static int (*write_to_log)(log_id_t, struct iovec *vec, size_t nr) = __write_to_ static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER; #endif -#define UNUSED __attribute__((__unused__)) +#ifndef __unused +#define __unused __attribute__((__unused__)) +#endif -static int log_fds[(int)LOG_ID_MAX] = { -1, -1, -1, -1 }; +#if FAKE_LOG_DEVICE +#define WEAK __attribute__((weak)) +static int log_fds[(int)LOG_ID_MAX] = { -1, -1, -1, -1, -1 }; +#else +static int logd_fd = -1; +#endif /* * This is used by the C++ code to decide if it should write logs through - * the C code. Basically, if /dev/log/... is available, we're running in + * the C code. Basically, if /dev/socket/logd is available, we're running in * the simulator rather than a desktop tool and want to use the device. */ static enum { @@ -71,7 +71,7 @@ static enum { int __android_log_dev_available(void) { if (g_log_status == kLogUninitialized) { - if (access("/dev/"LOGGER_LOG_MAIN, W_OK) == 0) + if (access("/dev/socket/logdw", W_OK) == 0) g_log_status = kLogAvailable; else g_log_status = kLogNotAvailable; @@ -80,30 +80,205 @@ int __android_log_dev_available(void) return (g_log_status == kLogAvailable); } -static int __write_to_log_null(log_id_t log_fd UNUSED, struct iovec *vec UNUSED, - size_t nr UNUSED) +#if !FAKE_LOG_DEVICE +/* give up, resources too limited */ +static int __write_to_log_null(log_id_t log_fd __unused, struct iovec *vec __unused, + size_t nr __unused) { return -1; } +#endif + +/* log_init_lock assumed */ +static int __write_to_log_initialize() +{ + int i, ret = 0; + +#if FAKE_LOG_DEVICE + for (i = 0; i < LOG_ID_MAX; i++) { + char buf[sizeof("/dev/log_system")]; + snprintf(buf, sizeof(buf), "/dev/log_%s", android_log_id_to_name(i)); + log_fds[i] = fakeLogOpen(buf, O_WRONLY); + } +#else + if (logd_fd >= 0) { + i = logd_fd; + logd_fd = -1; + close(i); + } + + i = socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0); + if (i < 0) { + ret = -errno; + write_to_log = __write_to_log_null; + } else if (fcntl(i, F_SETFL, O_NONBLOCK) < 0) { + ret = -errno; + close(i); + i = -1; + write_to_log = __write_to_log_null; + } else { + struct sockaddr_un un; + memset(&un, 0, sizeof(struct sockaddr_un)); + un.sun_family = AF_UNIX; + strcpy(un.sun_path, "/dev/socket/logdw"); + + if (connect(i, (struct sockaddr *)&un, sizeof(struct sockaddr_un)) < 0) { + ret = -errno; + close(i); + i = -1; + } + } + logd_fd = i; +#endif + + return ret; +} static int __write_to_log_kernel(log_id_t log_id, struct iovec *vec, size_t nr) { ssize_t ret; +#if FAKE_LOG_DEVICE int log_fd; if (/*(int)log_id >= 0 &&*/ (int)log_id < (int)LOG_ID_MAX) { log_fd = log_fds[(int)log_id]; } else { - return EBADF; + return -EBADF; } - do { - ret = log_writev(log_fd, vec, nr); - } while (ret < 0 && errno == EINTR); + ret = fakeLogWritev(log_fd, vec, nr); + if (ret < 0) { + ret = -errno; + } + } while (ret == -EINTR); +#else + static const unsigned header_length = 3; + struct iovec newVec[nr + header_length]; + typeof_log_id_t log_id_buf; + uint16_t tid; + struct timespec ts; + log_time realtime_ts; + size_t i, payload_size; + static uid_t last_uid = AID_ROOT; /* logd *always* starts up as AID_ROOT */ + + if (last_uid == AID_ROOT) { /* have we called to get the UID yet? */ + last_uid = getuid(); + } + if (last_uid == AID_LOGD) { /* logd, after initialization and priv drop */ + /* + * ignore log messages we send to ourself (logd). + * Such log messages are often generated by libraries we depend on + * which use standard Android logging. + */ + return 0; + } + + if (logd_fd < 0) { + return -EBADF; + } + + /* + * struct { + * // what we provide + * typeof_log_id_t log_id; + * u16 tid; + * log_time realtime; + * // caller provides + * union { + * struct { + * char prio; + * char payload[]; + * } string; + * struct { + * uint32_t tag + * char payload[]; + * } binary; + * }; + * }; + */ + + clock_gettime(CLOCK_REALTIME, &ts); + realtime_ts.tv_sec = ts.tv_sec; + realtime_ts.tv_nsec = ts.tv_nsec; + + log_id_buf = log_id; + tid = gettid(); + + newVec[0].iov_base = (unsigned char *) &log_id_buf; + newVec[0].iov_len = sizeof_log_id_t; + newVec[1].iov_base = (unsigned char *) &tid; + newVec[1].iov_len = sizeof(tid); + newVec[2].iov_base = (unsigned char *) &realtime_ts; + newVec[2].iov_len = sizeof(log_time); + + for (payload_size = 0, i = header_length; i < nr + header_length; i++) { + newVec[i].iov_base = vec[i - header_length].iov_base; + payload_size += newVec[i].iov_len = vec[i - header_length].iov_len; + + if (payload_size > LOGGER_ENTRY_MAX_PAYLOAD) { + newVec[i].iov_len -= payload_size - LOGGER_ENTRY_MAX_PAYLOAD; + if (newVec[i].iov_len) { + ++i; + } + break; + } + } + + /* + * The write below could be lost, but will never block. + * + * ENOTCONN occurs if logd dies. + * EAGAIN occurs if logd is overloaded. + */ + ret = writev(logd_fd, newVec, i); + if (ret < 0) { + ret = -errno; + if (ret == -ENOTCONN) { +#ifdef HAVE_PTHREADS + pthread_mutex_lock(&log_init_lock); +#endif + ret = __write_to_log_initialize(); +#ifdef HAVE_PTHREADS + pthread_mutex_unlock(&log_init_lock); +#endif + + if (ret < 0) { + return ret; + } + + ret = writev(logd_fd, newVec, nr + header_length); + if (ret < 0) { + ret = -errno; + } + } + } + + if (ret > (ssize_t)(sizeof_log_id_t + sizeof(tid) + sizeof(log_time))) { + ret -= sizeof_log_id_t + sizeof(tid) + sizeof(log_time); + } +#endif return ret; } +#if FAKE_LOG_DEVICE +static const char *LOG_NAME[LOG_ID_MAX] = { + [LOG_ID_MAIN] = "main", + [LOG_ID_RADIO] = "radio", + [LOG_ID_EVENTS] = "events", + [LOG_ID_SYSTEM] = "system", + [LOG_ID_CRASH] = "crash" +}; + +const WEAK char *android_log_id_to_name(log_id_t log_id) +{ + if (log_id >= LOG_ID_MAX) { + log_id = LOG_ID_MAIN; + } + return LOG_NAME[log_id]; +} +#endif + static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr) { #ifdef HAVE_PTHREADS @@ -111,27 +286,17 @@ static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr) #endif if (write_to_log == __write_to_log_init) { - log_fds[LOG_ID_MAIN] = log_open("/dev/"LOGGER_LOG_MAIN, O_WRONLY); - log_fds[LOG_ID_RADIO] = log_open("/dev/"LOGGER_LOG_RADIO, O_WRONLY); - log_fds[LOG_ID_EVENTS] = log_open("/dev/"LOGGER_LOG_EVENTS, O_WRONLY); - log_fds[LOG_ID_SYSTEM] = log_open("/dev/"LOGGER_LOG_SYSTEM, O_WRONLY); - - write_to_log = __write_to_log_kernel; + int ret; - if (log_fds[LOG_ID_MAIN] < 0 || log_fds[LOG_ID_RADIO] < 0 || - log_fds[LOG_ID_EVENTS] < 0) { - log_close(log_fds[LOG_ID_MAIN]); - log_close(log_fds[LOG_ID_RADIO]); - log_close(log_fds[LOG_ID_EVENTS]); - log_fds[LOG_ID_MAIN] = -1; - log_fds[LOG_ID_RADIO] = -1; - log_fds[LOG_ID_EVENTS] = -1; - write_to_log = __write_to_log_null; + ret = __write_to_log_initialize(); + if (ret < 0) { +#ifdef HAVE_PTHREADS + pthread_mutex_unlock(&log_init_lock); +#endif + return ret; } - if (log_fds[LOG_ID_SYSTEM] < 0) { - log_fds[LOG_ID_SYSTEM] = log_fds[LOG_ID_MAIN]; - } + write_to_log = __write_to_log_kernel; } #ifdef HAVE_PTHREADS @@ -161,11 +326,18 @@ int __android_log_write(int prio, const char *tag, const char *msg) !strcmp(tag, "PHONE") || !strcmp(tag, "SMS")) { log_id = LOG_ID_RADIO; - // Inform third party apps/ril/radio.. to use Rlog or RLOG + /* Inform third party apps/ril/radio.. to use Rlog or RLOG */ snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag); tag = tmp_tag; } +#if __BIONIC__ + if (prio == ANDROID_LOG_FATAL) { + extern void __android_set_abort_message(const char*); + __android_set_abort_message(msg); + } +#endif + vec[0].iov_base = (unsigned char *) &prio; vec[0].iov_len = 1; vec[1].iov_base = (void *) tag; @@ -196,7 +368,7 @@ int __android_log_buf_write(int bufID, int prio, const char *tag, const char *ms !strcmp(tag, "PHONE") || !strcmp(tag, "SMS"))) { bufID = LOG_ID_RADIO; - // Inform third party apps/ril/radio.. to use Rlog or RLOG + /* Inform third party apps/ril/radio.. to use Rlog or RLOG */ snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag); tag = tmp_tag; } @@ -266,8 +438,8 @@ void __android_log_assert(const char *cond, const char *tag, } __android_log_write(ANDROID_LOG_FATAL, tag, buf); - __builtin_trap(); /* trap so we have a chance to debug the situation */ + /* NOTREACHED */ } int __android_log_bwrite(int32_t tag, const void *payload, size_t len) @@ -288,7 +460,7 @@ int __android_log_bwrite(int32_t tag, const void *payload, size_t len) * handy if we just want to dump an integer into the log. */ int __android_log_btwrite(int32_t tag, char type, const void *payload, - size_t len) + size_t len) { struct iovec vec[3]; @@ -301,3 +473,25 @@ int __android_log_btwrite(int32_t tag, char type, const void *payload, return write_to_log(LOG_ID_EVENTS, vec, 3); } + +/* + * Like __android_log_bwrite, but used for writing strings to the + * event log. + */ +int __android_log_bswrite(int32_t tag, const char *payload) +{ + struct iovec vec[4]; + char type = EVENT_TYPE_STRING; + uint32_t len = strlen(payload); + + vec[0].iov_base = &tag; + vec[0].iov_len = sizeof(tag); + vec[1].iov_base = &type; + vec[1].iov_len = sizeof(type); + vec[2].iov_base = &len; + vec[2].iov_len = sizeof(len); + vec[3].iov_base = (void*)payload; + vec[3].iov_len = len; + + return write_to_log(LOG_ID_EVENTS, vec, 4); +} diff --git a/liblog/logd_write_kern.c b/liblog/logd_write_kern.c new file mode 100644 index 0000000..1ed5ecf --- /dev/null +++ b/liblog/logd_write_kern.c @@ -0,0 +1,341 @@ +/* + * Copyright (C) 2007-2014 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 <fcntl.h> +#ifdef HAVE_PTHREADS +#include <pthread.h> +#endif +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <time.h> +#include <unistd.h> + +#include <log/log.h> +#include <log/logd.h> +#include <log/logger.h> + +#define LOGGER_LOG_MAIN "log/main" +#define LOGGER_LOG_RADIO "log/radio" +#define LOGGER_LOG_EVENTS "log/events" +#define LOGGER_LOG_SYSTEM "log/system" + +#define LOG_BUF_SIZE 1024 + +#if FAKE_LOG_DEVICE +/* This will be defined when building for the host. */ +#include "fake_log_device.h" +#define log_open(pathname, flags) fakeLogOpen(pathname, flags) +#define log_writev(filedes, vector, count) fakeLogWritev(filedes, vector, count) +#define log_close(filedes) fakeLogClose(filedes) +#else +#define log_open(pathname, flags) open(pathname, (flags) | O_CLOEXEC) +#define log_writev(filedes, vector, count) writev(filedes, vector, count) +#define log_close(filedes) close(filedes) +#endif + +static int __write_to_log_init(log_id_t, struct iovec *vec, size_t nr); +static int (*write_to_log)(log_id_t, struct iovec *vec, size_t nr) = __write_to_log_init; +#ifdef HAVE_PTHREADS +static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER; +#endif + +#ifndef __unused +#define __unused __attribute__((__unused__)) +#endif + +static int log_fds[(int)LOG_ID_MAX] = { -1, -1, -1, -1 }; + +/* + * This is used by the C++ code to decide if it should write logs through + * the C code. Basically, if /dev/log/... is available, we're running in + * the simulator rather than a desktop tool and want to use the device. + */ +static enum { + kLogUninitialized, kLogNotAvailable, kLogAvailable +} g_log_status = kLogUninitialized; +int __android_log_dev_available(void) +{ + if (g_log_status == kLogUninitialized) { + if (access("/dev/"LOGGER_LOG_MAIN, W_OK) == 0) + g_log_status = kLogAvailable; + else + g_log_status = kLogNotAvailable; + } + + return (g_log_status == kLogAvailable); +} + +static int __write_to_log_null(log_id_t log_fd __unused, struct iovec *vec __unused, + size_t nr __unused) +{ + return -1; +} + +static int __write_to_log_kernel(log_id_t log_id, struct iovec *vec, size_t nr) +{ + ssize_t ret; + int log_fd; + + if (/*(int)log_id >= 0 &&*/ (int)log_id < (int)LOG_ID_MAX) { + if (log_id == LOG_ID_CRASH) { + log_id = LOG_ID_MAIN; + } + log_fd = log_fds[(int)log_id]; + } else { + return -EBADF; + } + + do { + ret = log_writev(log_fd, vec, nr); + if (ret < 0) { + ret = -errno; + } + } while (ret == -EINTR); + + return ret; +} + +static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr) +{ +#ifdef HAVE_PTHREADS + pthread_mutex_lock(&log_init_lock); +#endif + + if (write_to_log == __write_to_log_init) { + log_fds[LOG_ID_MAIN] = log_open("/dev/"LOGGER_LOG_MAIN, O_WRONLY); + log_fds[LOG_ID_RADIO] = log_open("/dev/"LOGGER_LOG_RADIO, O_WRONLY); + log_fds[LOG_ID_EVENTS] = log_open("/dev/"LOGGER_LOG_EVENTS, O_WRONLY); + log_fds[LOG_ID_SYSTEM] = log_open("/dev/"LOGGER_LOG_SYSTEM, O_WRONLY); + + write_to_log = __write_to_log_kernel; + + if (log_fds[LOG_ID_MAIN] < 0 || log_fds[LOG_ID_RADIO] < 0 || + log_fds[LOG_ID_EVENTS] < 0) { + log_close(log_fds[LOG_ID_MAIN]); + log_close(log_fds[LOG_ID_RADIO]); + log_close(log_fds[LOG_ID_EVENTS]); + log_fds[LOG_ID_MAIN] = -1; + log_fds[LOG_ID_RADIO] = -1; + log_fds[LOG_ID_EVENTS] = -1; + write_to_log = __write_to_log_null; + } + + if (log_fds[LOG_ID_SYSTEM] < 0) { + log_fds[LOG_ID_SYSTEM] = log_fds[LOG_ID_MAIN]; + } + } + +#ifdef HAVE_PTHREADS + pthread_mutex_unlock(&log_init_lock); +#endif + + return write_to_log(log_id, vec, nr); +} + +int __android_log_write(int prio, const char *tag, const char *msg) +{ + struct iovec vec[3]; + log_id_t log_id = LOG_ID_MAIN; + char tmp_tag[32]; + + if (!tag) + tag = ""; + + /* XXX: This needs to go! */ + if (!strcmp(tag, "HTC_RIL") || + !strncmp(tag, "RIL", 3) || /* Any log tag with "RIL" as the prefix */ + !strncmp(tag, "IMS", 3) || /* Any log tag with "IMS" as the prefix */ + !strcmp(tag, "AT") || + !strcmp(tag, "GSM") || + !strcmp(tag, "STK") || + !strcmp(tag, "CDMA") || + !strcmp(tag, "PHONE") || + !strcmp(tag, "SMS")) { + log_id = LOG_ID_RADIO; + /* Inform third party apps/ril/radio.. to use Rlog or RLOG */ + snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag); + tag = tmp_tag; + } + +#if __BIONIC__ + if (prio == ANDROID_LOG_FATAL) { + extern void __android_set_abort_message(const char*); + __android_set_abort_message(msg); + } +#endif + + vec[0].iov_base = (unsigned char *) &prio; + vec[0].iov_len = 1; + vec[1].iov_base = (void *) tag; + vec[1].iov_len = strlen(tag) + 1; + vec[2].iov_base = (void *) msg; + vec[2].iov_len = strlen(msg) + 1; + + return write_to_log(log_id, vec, 3); +} + +int __android_log_buf_write(int bufID, int prio, const char *tag, const char *msg) +{ + struct iovec vec[3]; + char tmp_tag[32]; + + if (!tag) + tag = ""; + + /* XXX: This needs to go! */ + if ((bufID != LOG_ID_RADIO) && + (!strcmp(tag, "HTC_RIL") || + !strncmp(tag, "RIL", 3) || /* Any log tag with "RIL" as the prefix */ + !strncmp(tag, "IMS", 3) || /* Any log tag with "IMS" as the prefix */ + !strcmp(tag, "AT") || + !strcmp(tag, "GSM") || + !strcmp(tag, "STK") || + !strcmp(tag, "CDMA") || + !strcmp(tag, "PHONE") || + !strcmp(tag, "SMS"))) { + bufID = LOG_ID_RADIO; + /* Inform third party apps/ril/radio.. to use Rlog or RLOG */ + snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag); + tag = tmp_tag; + } + + vec[0].iov_base = (unsigned char *) &prio; + vec[0].iov_len = 1; + vec[1].iov_base = (void *) tag; + vec[1].iov_len = strlen(tag) + 1; + vec[2].iov_base = (void *) msg; + vec[2].iov_len = strlen(msg) + 1; + + return write_to_log(bufID, vec, 3); +} + +int __android_log_vprint(int prio, const char *tag, const char *fmt, va_list ap) +{ + char buf[LOG_BUF_SIZE]; + + vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); + + return __android_log_write(prio, tag, buf); +} + +int __android_log_print(int prio, const char *tag, const char *fmt, ...) +{ + va_list ap; + char buf[LOG_BUF_SIZE]; + + va_start(ap, fmt); + vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); + va_end(ap); + + return __android_log_write(prio, tag, buf); +} + +int __android_log_buf_print(int bufID, int prio, const char *tag, const char *fmt, ...) +{ + va_list ap; + char buf[LOG_BUF_SIZE]; + + va_start(ap, fmt); + vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); + va_end(ap); + + return __android_log_buf_write(bufID, prio, tag, buf); +} + +void __android_log_assert(const char *cond, const char *tag, + const char *fmt, ...) +{ + char buf[LOG_BUF_SIZE]; + + if (fmt) { + va_list ap; + va_start(ap, fmt); + vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); + va_end(ap); + } else { + /* Msg not provided, log condition. N.B. Do not use cond directly as + * format string as it could contain spurious '%' syntax (e.g. + * "%d" in "blocks%devs == 0"). + */ + if (cond) + snprintf(buf, LOG_BUF_SIZE, "Assertion failed: %s", cond); + else + strcpy(buf, "Unspecified assertion failed"); + } + + __android_log_write(ANDROID_LOG_FATAL, tag, buf); + __builtin_trap(); /* trap so we have a chance to debug the situation */ + /* NOTREACHED */ +} + +int __android_log_bwrite(int32_t tag, const void *payload, size_t len) +{ + struct iovec vec[2]; + + vec[0].iov_base = &tag; + vec[0].iov_len = sizeof(tag); + vec[1].iov_base = (void*)payload; + vec[1].iov_len = len; + + return write_to_log(LOG_ID_EVENTS, vec, 2); +} + +/* + * Like __android_log_bwrite, but takes the type as well. Doesn't work + * for the general case where we're generating lists of stuff, but very + * handy if we just want to dump an integer into the log. + */ +int __android_log_btwrite(int32_t tag, char type, const void *payload, + size_t len) +{ + struct iovec vec[3]; + + vec[0].iov_base = &tag; + vec[0].iov_len = sizeof(tag); + vec[1].iov_base = &type; + vec[1].iov_len = sizeof(type); + vec[2].iov_base = (void*)payload; + vec[2].iov_len = len; + + return write_to_log(LOG_ID_EVENTS, vec, 3); +} + +/* + * Like __android_log_bwrite, but used for writing strings to the + * event log. + */ +int __android_log_bswrite(int32_t tag, const char *payload) +{ + struct iovec vec[4]; + char type = EVENT_TYPE_STRING; + uint32_t len = strlen(payload); + + vec[0].iov_base = &tag; + vec[0].iov_len = sizeof(tag); + vec[1].iov_base = &type; + vec[1].iov_len = sizeof(type); + vec[2].iov_base = &len; + vec[2].iov_len = sizeof(len); + vec[3].iov_base = (void*)payload; + vec[3].iov_len = len; + + return write_to_log(LOG_ID_EVENTS, vec, 4); +} diff --git a/liblog/logprint.c b/liblog/logprint.c index a7480d5..08e830a 100644 --- a/liblog/logprint.c +++ b/liblog/logprint.c @@ -17,14 +17,14 @@ #define _GNU_SOURCE /* for asprintf */ +#include <arpa/inet.h> +#include <assert.h> #include <ctype.h> -#include <stdio.h> #include <errno.h> -#include <stdlib.h> #include <stdint.h> +#include <stdio.h> +#include <stdlib.h> #include <string.h> -#include <assert.h> -#include <arpa/inet.h> #include <log/logd.h> #include <log/logprint.h> @@ -52,15 +52,7 @@ static FilterInfo * filterinfo_new(const char * tag, android_LogPriority pri) return p_ret; } -static void filterinfo_free(FilterInfo *p_info) -{ - if (p_info == NULL) { - return; - } - - free(p_info->mTag); - p_info->mTag = NULL; -} +/* balance to above, filterinfo_free left unimplemented */ /* * Note: also accepts 0-9 priorities @@ -139,23 +131,6 @@ static android_LogPriority filterPriForTag( return p_format->global_pri; } -/** for debugging */ -static void dumpFilters(AndroidLogFormat *p_format) -{ - FilterInfo *p_fi; - - for (p_fi = p_format->filters ; p_fi != NULL ; p_fi = p_fi->p_next) { - char cPri = filterPriToChar(p_fi->mPri); - if (p_fi->mPri == ANDROID_LOG_DEFAULT) { - cPri = filterPriToChar(p_format->global_pri); - } - fprintf(stderr,"%s:%c\n", p_fi->mTag, cPri); - } - - fprintf(stderr,"*:%c\n", filterPriToChar(p_format->global_pri)); - -} - /** * returns 1 if this log line should be printed based on its priority * and tag, and 0 if it should not @@ -234,7 +209,6 @@ AndroidLogPrintFormat android_log_formatFromString(const char * formatString) int android_log_addFilterRule(AndroidLogFormat *p_format, const char *filterExpression) { - size_t i=0; size_t tagNameLength; android_LogPriority pri = ANDROID_LOG_DEFAULT; @@ -718,7 +692,6 @@ char *android_log_formatLogLine ( #endif struct tm* ptm; char timeBuf[32]; - char headerBuf[128]; char prefixBuf[128], suffixBuf[128]; char priChar; int prefixSuffixIsHeaderFooter = 0; @@ -817,7 +790,6 @@ char *android_log_formatLogLine ( /* the following code is tragically unreadable */ size_t numLines; - size_t i; char *p; size_t bufferSize; const char *pm; @@ -938,88 +910,3 @@ done: return ret; } - - - -void logprint_run_tests() -{ -#if 0 - - fprintf(stderr, "tests disabled\n"); - -#else - - int err; - const char *tag; - AndroidLogFormat *p_format; - - p_format = android_log_format_new(); - - fprintf(stderr, "running tests\n"); - - tag = "random"; - - android_log_addFilterRule(p_format,"*:i"); - - assert (ANDROID_LOG_INFO == filterPriForTag(p_format, "random")); - assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0); - android_log_addFilterRule(p_format, "*"); - assert (ANDROID_LOG_DEBUG == filterPriForTag(p_format, "random")); - assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0); - android_log_addFilterRule(p_format, "*:v"); - assert (ANDROID_LOG_VERBOSE == filterPriForTag(p_format, "random")); - assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0); - android_log_addFilterRule(p_format, "*:i"); - assert (ANDROID_LOG_INFO == filterPriForTag(p_format, "random")); - assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0); - - android_log_addFilterRule(p_format, "random"); - assert (ANDROID_LOG_VERBOSE == filterPriForTag(p_format, "random")); - assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0); - android_log_addFilterRule(p_format, "random:v"); - assert (ANDROID_LOG_VERBOSE == filterPriForTag(p_format, "random")); - assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0); - android_log_addFilterRule(p_format, "random:d"); - assert (ANDROID_LOG_DEBUG == filterPriForTag(p_format, "random")); - assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0); - android_log_addFilterRule(p_format, "random:w"); - assert (ANDROID_LOG_WARN == filterPriForTag(p_format, "random")); - assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0); - - android_log_addFilterRule(p_format, "crap:*"); - assert (ANDROID_LOG_VERBOSE== filterPriForTag(p_format, "crap")); - assert(android_log_shouldPrintLine(p_format, "crap", ANDROID_LOG_VERBOSE) > 0); - - // invalid expression - err = android_log_addFilterRule(p_format, "random:z"); - assert (err < 0); - assert (ANDROID_LOG_WARN == filterPriForTag(p_format, "random")); - assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0); - - // Issue #550946 - err = android_log_addFilterString(p_format, " "); - assert(err == 0); - assert(ANDROID_LOG_WARN == filterPriForTag(p_format, "random")); - - // note trailing space - err = android_log_addFilterString(p_format, "*:s random:d "); - assert(err == 0); - assert(ANDROID_LOG_DEBUG == filterPriForTag(p_format, "random")); - - err = android_log_addFilterString(p_format, "*:s random:z"); - assert(err < 0); - - -#if 0 - char *ret; - char defaultBuffer[512]; - - ret = android_log_formatLogLine(p_format, - defaultBuffer, sizeof(defaultBuffer), 0, ANDROID_LOG_ERROR, 123, - 123, 123, "random", "nofile", strlen("Hello"), "Hello", NULL); -#endif - - - fprintf(stderr, "tests complete\n"); -#endif -} diff --git a/liblog/tests/Android.mk b/liblog/tests/Android.mk index db06cf7..255dc2e 100644 --- a/liblog/tests/Android.mk +++ b/liblog/tests/Android.mk @@ -32,7 +32,7 @@ benchmark_c_flags := \ benchmark_src_files := \ benchmark_main.cpp \ - liblog_benchmark.cpp \ + liblog_benchmark.cpp # Build benchmarks for the device. Run with: # adb shell liblog-benchmarks @@ -59,10 +59,22 @@ test_c_flags := \ -g \ -Wall -Wextra \ -Werror \ - -fno-builtin \ + -fno-builtin test_src_files := \ - liblog_test.cpp \ + liblog_test.cpp + +# to prevent breaking the build if bionic not relatively visible to us +ifneq ($(wildcard $(LOCAL_PATH)/../../../../bionic/libc/bionic/libc_logging.cpp),) + +test_src_files += \ + libc_test.cpp + +ifndef ($(TARGET_USES_LOGD),false) +test_c_flags += -DTARGET_USES_LOGD +endif + +endif # Build tests for the device (with .so). Run with: # adb shell /data/nativetest/liblog-unit-tests/liblog-unit-tests @@ -71,7 +83,6 @@ LOCAL_MODULE := $(test_module_prefix)unit-tests LOCAL_MODULE_TAGS := $(test_tags) LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk LOCAL_CFLAGS += $(test_c_flags) -LOCAL_LDLIBS := -lpthread LOCAL_SHARED_LIBRARIES := liblog LOCAL_SRC_FILES := $(test_src_files) include $(BUILD_NATIVE_TEST) diff --git a/liblog/tests/benchmark_main.cpp b/liblog/tests/benchmark_main.cpp index 02df460..090394c 100644 --- a/liblog/tests/benchmark_main.cpp +++ b/liblog/tests/benchmark_main.cpp @@ -16,6 +16,7 @@ #include <benchmark.h> +#include <inttypes.h> #include <regex.h> #include <stdio.h> #include <stdlib.h> @@ -158,10 +159,10 @@ void Run(Benchmark* b) { sdev = (sqrt((double)nXvariance) / gBenchmarkNum / gBenchmarkNum) + 0.5; } if (mean > (10000 * sdev)) { - printf("%-25s %10llu %10llu%s\n", full_name, + printf("%-25s %10" PRIu64 " %10" PRIu64 "%s\n", full_name, static_cast<uint64_t>(iterations), mean, throughput); } else { - printf("%-25s %10llu %10llu(\317\203%llu)%s\n", full_name, + printf("%-25s %10" PRIu64 " %10" PRIu64 "(\317\203%" PRIu64 ")%s\n", full_name, static_cast<uint64_t>(iterations), mean, sdev, throughput); } fflush(stdout); diff --git a/liblog/tests/libc_test.cpp b/liblog/tests/libc_test.cpp new file mode 100644 index 0000000..9839729 --- /dev/null +++ b/liblog/tests/libc_test.cpp @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2014 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 <fcntl.h> +#include <sys/cdefs.h> + +#include <gtest/gtest.h> + +// Should be in bionic test suite, *but* we are using liblog to confirm +// end-to-end logging, so let the overly cute oedipus complex begin ... +#include "../../../../bionic/libc/bionic/libc_logging.cpp" // not Standalone +#define _ANDROID_LOG_H // Priorities redefined +#define _LIBS_LOG_LOG_H // log ids redefined +typedef unsigned char log_id_t; // log_id_t missing as a result +#ifdef TARGET_USES_LOGD +#define _LIBS_LOG_LOG_READ_H // log_time redefined +#endif + +#include <log/log.h> +#include <log/logger.h> +#include <log/log_read.h> + +TEST(libc, __libc_android_log_event_int) { + struct logger_list *logger_list; + + pid_t pid = getpid(); + + ASSERT_TRUE(NULL != (logger_list = android_logger_list_open( + LOG_ID_EVENTS, O_RDONLY | O_NDELAY, 1000, pid))); + + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + int value = ts.tv_nsec; + + __libc_android_log_event_int(0, value); + usleep(1000000); + + int count = 0; + + for (;;) { + log_msg log_msg; + if (android_logger_list_read(logger_list, &log_msg) <= 0) { + break; + } + + ASSERT_EQ(log_msg.entry.pid, pid); + + if ((log_msg.entry.len != (4 + 1 + 4)) + || ((int)log_msg.id() != LOG_ID_EVENTS)) { + continue; + } + + char *eventData = log_msg.msg(); + + int incoming = (eventData[0] & 0xFF) | + ((eventData[1] & 0xFF) << 8) | + ((eventData[2] & 0xFF) << 16) | + ((eventData[3] & 0xFF) << 24); + + if (incoming != 0) { + continue; + } + + if (eventData[4] != EVENT_TYPE_INT) { + continue; + } + + incoming = (eventData[4 + 1 + 0] & 0xFF) | + ((eventData[4 + 1 + 1] & 0xFF) << 8) | + ((eventData[4 + 1 + 2] & 0xFF) << 16) | + ((eventData[4 + 1 + 3] & 0xFF) << 24); + + if (incoming == value) { + ++count; + } + } + + EXPECT_EQ(1, count); + + android_logger_list_close(logger_list); +} + +TEST(libc, __libc_fatal_no_abort) { + struct logger_list *logger_list; + + pid_t pid = getpid(); + + ASSERT_TRUE(NULL != (logger_list = android_logger_list_open( + (log_id_t)LOG_ID_CRASH, O_RDONLY | O_NDELAY, 1000, pid))); + + char b[80]; + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + + __libc_fatal_no_abort("%u.%09u", (unsigned)ts.tv_sec, (unsigned)ts.tv_nsec); + snprintf(b, sizeof(b),"%u.%09u", (unsigned)ts.tv_sec, (unsigned)ts.tv_nsec); + usleep(1000000); + + int count = 0; + + for (;;) { + log_msg log_msg; + if (android_logger_list_read(logger_list, &log_msg) <= 0) { + break; + } + + ASSERT_EQ(log_msg.entry.pid, pid); + + if ((int)log_msg.id() != LOG_ID_CRASH) { + continue; + } + + char *data = log_msg.msg(); + + if ((*data == ANDROID_LOG_FATAL) + && !strcmp(data + 1, "libc") + && !strcmp(data + 1 + strlen(data + 1) + 1, b)) { + ++count; + } + } + + EXPECT_EQ(1, count); + + android_logger_list_close(logger_list); +} diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp index 19406fb..549d79e 100644 --- a/liblog/tests/liblog_benchmark.cpp +++ b/liblog/tests/liblog_benchmark.cpp @@ -99,7 +99,7 @@ static void BM_log_overhead(int iters) { } BENCHMARK(BM_log_overhead); -static void caught_latency(int signum) +static void caught_latency(int /*signum*/) { unsigned long long v = 0xDEADBEEFA55A5AA5ULL; @@ -143,7 +143,7 @@ static void BM_log_latency(int iters) { for (int j = 0, i = 0; i < iters && j < 10*iters; ++i, ++j) { log_time ts; LOG_FAILURE_RETRY(( - clock_gettime(CLOCK_REALTIME, &ts), + ts = log_time(CLOCK_REALTIME), android_btWriteLog(0, EVENT_TYPE_LONG, &ts, sizeof(ts)))); for (;;) { @@ -193,7 +193,7 @@ static void BM_log_latency(int iters) { } BENCHMARK(BM_log_latency); -static void caught_delay(int signum) +static void caught_delay(int /*signum*/) { unsigned long long v = 0xDEADBEEFA55A5AA6ULL; diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp index 9ae8f22..393e2cd 100644 --- a/liblog/tests/liblog_test.cpp +++ b/liblog/tests/liblog_test.cpp @@ -15,11 +15,13 @@ */ #include <fcntl.h> +#include <inttypes.h> #include <signal.h> #include <gtest/gtest.h> #include <log/log.h> #include <log/logger.h> #include <log/log_read.h> +#include <log/logprint.h> // enhanced version of LOG_FAILURE_RETRY to add support for EAGAIN and // non-syscall libs. Since we are only using this in the emergency of @@ -37,30 +39,30 @@ _rc; }) TEST(liblog, __android_log_buf_print) { - ASSERT_LT(0, __android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO, + EXPECT_LT(0, __android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO, "TEST__android_log_buf_print", "radio")); usleep(1000); - ASSERT_LT(0, __android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO, + EXPECT_LT(0, __android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO, "TEST__android_log_buf_print", "system")); usleep(1000); - ASSERT_LT(0, __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_INFO, + EXPECT_LT(0, __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_INFO, "TEST__android_log_buf_print", "main")); usleep(1000); } TEST(liblog, __android_log_buf_write) { - ASSERT_LT(0, __android_log_buf_write(LOG_ID_RADIO, ANDROID_LOG_INFO, + EXPECT_LT(0, __android_log_buf_write(LOG_ID_RADIO, ANDROID_LOG_INFO, "TEST__android_log_buf_write", "radio")); usleep(1000); - ASSERT_LT(0, __android_log_buf_write(LOG_ID_SYSTEM, ANDROID_LOG_INFO, + EXPECT_LT(0, __android_log_buf_write(LOG_ID_SYSTEM, ANDROID_LOG_INFO, "TEST__android_log_buf_write", "system")); usleep(1000); - ASSERT_LT(0, __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_INFO, + EXPECT_LT(0, __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_INFO, "TEST__android_log_buf_write", "main")); usleep(1000); @@ -68,16 +70,16 @@ TEST(liblog, __android_log_buf_write) { TEST(liblog, __android_log_btwrite) { int intBuf = 0xDEADBEEF; - ASSERT_LT(0, __android_log_btwrite(0, + EXPECT_LT(0, __android_log_btwrite(0, EVENT_TYPE_INT, &intBuf, sizeof(intBuf))); long long longBuf = 0xDEADBEEFA55A5AA5; - ASSERT_LT(0, __android_log_btwrite(0, + EXPECT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &longBuf, sizeof(longBuf))); usleep(1000); char Buf[] = "\20\0\0\0DeAdBeEfA55a5aA5"; - ASSERT_LT(0, __android_log_btwrite(0, + EXPECT_LT(0, __android_log_btwrite(0, EVENT_TYPE_STRING, Buf, sizeof(Buf) - 1)); usleep(1000); @@ -85,8 +87,8 @@ TEST(liblog, __android_log_btwrite) { static void* ConcurrentPrintFn(void *arg) { int ret = __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_INFO, - "TEST__android_log_print", "Concurrent %d", - reinterpret_cast<int>(arg)); + "TEST__android_log_print", "Concurrent %" PRIuPTR, + reinterpret_cast<uintptr_t>(arg)); return reinterpret_cast<void*>(ret); } @@ -106,8 +108,9 @@ TEST(liblog, concurrent_name(__android_log_buf_print, NUM_CONCURRENT)) { for (i=0; i < NUM_CONCURRENT; i++) { void* result; ASSERT_EQ(0, pthread_join(t[i], &result)); - if ((0 == ret) && (0 != reinterpret_cast<int>(result))) { - ret = reinterpret_cast<int>(result); + int this_result = reinterpret_cast<uintptr_t>(result); + if ((0 == ret) && (0 != this_result)) { + ret = this_result; } } ASSERT_LT(0, ret); @@ -118,7 +121,7 @@ TEST(liblog, __android_log_btwrite__android_logger_list_read) { pid_t pid = getpid(); - ASSERT_EQ(0, NULL == (logger_list = android_logger_list_open( + ASSERT_TRUE(NULL != (logger_list = android_logger_list_open( LOG_ID_EVENTS, O_RDONLY | O_NDELAY, 1000, pid))); log_time ts(CLOCK_MONOTONIC); @@ -153,7 +156,7 @@ TEST(liblog, __android_log_btwrite__android_logger_list_read) { } } - ASSERT_EQ(1, count); + EXPECT_EQ(1, count); android_logger_list_close(logger_list); } @@ -161,7 +164,7 @@ TEST(liblog, __android_log_btwrite__android_logger_list_read) { static unsigned signaled; log_time signal_time; -static void caught_blocking(int signum) +static void caught_blocking(int /*signum*/) { unsigned long long v = 0xDEADBEEFA55A0000ULL; @@ -169,7 +172,7 @@ static void caught_blocking(int signum) ++signaled; if ((signal_time.tv_sec == 0) && (signal_time.tv_nsec == 0)) { - clock_gettime(CLOCK_MONOTONIC, &signal_time); + signal_time = log_time(CLOCK_MONOTONIC); signal_time.tv_sec += 2; } @@ -219,7 +222,7 @@ TEST(liblog, android_logger_list_read__cpu) { v += pid & 0xFFFF; - ASSERT_EQ(0, NULL == (logger_list = android_logger_list_open( + ASSERT_TRUE(NULL != (logger_list = android_logger_list_open( LOG_ID_EVENTS, O_RDONLY, 1000, pid))); int count = 0; @@ -275,13 +278,13 @@ TEST(liblog, android_logger_list_read__cpu) { ++signals; break; } - } while (!signaled || ({log_time t(CLOCK_MONOTONIC); t < signal_time;})); + } while (!signaled || (log_time(CLOCK_MONOTONIC) < signal_time)); alarm(0); signal(SIGALRM, SIG_DFL); - ASSERT_LT(1, count); + EXPECT_LT(1, count); - ASSERT_EQ(1, signals); + EXPECT_EQ(1, signals); android_logger_list_close(logger_list); @@ -293,9 +296,302 @@ TEST(liblog, android_logger_list_read__cpu) { const unsigned long long one_percent_ticks = alarm_time; unsigned long long user_ticks = uticks_end - uticks_start; unsigned long long system_ticks = sticks_end - sticks_start; - ASSERT_GT(one_percent_ticks, user_ticks); - ASSERT_GT(one_percent_ticks, system_ticks); - ASSERT_GT(one_percent_ticks, user_ticks + system_ticks); + EXPECT_GT(one_percent_ticks, user_ticks); + EXPECT_GT(one_percent_ticks, system_ticks); + EXPECT_GT(one_percent_ticks, user_ticks + system_ticks); +} + +static const char max_payload_tag[] = "TEST_max_payload_XXXX"; +static const char max_payload_buf[LOGGER_ENTRY_MAX_PAYLOAD + - sizeof(max_payload_tag) - 1] = "LEONATO\n\ +I learn in this letter that Don Peter of Arragon\n\ +comes this night to Messina\n\ +MESSENGER\n\ +He is very near by this: he was not three leagues off\n\ +when I left him\n\ +LEONATO\n\ +How many gentlemen have you lost in this action?\n\ +MESSENGER\n\ +But few of any sort, and none of name\n\ +LEONATO\n\ +A victory is twice itself when the achiever brings\n\ +home full numbers. I find here that Don Peter hath\n\ +bestowed much honour on a young Florentine called Claudio\n\ +MESSENGER\n\ +Much deserved on his part and equally remembered by\n\ +Don Pedro: he hath borne himself beyond the\n\ +promise of his age, doing, in the figure of a lamb,\n\ +the feats of a lion: he hath indeed better\n\ +bettered expectation than you must expect of me to\n\ +tell you how\n\ +LEONATO\n\ +He hath an uncle here in Messina will be very much\n\ +glad of it.\n\ +MESSENGER\n\ +I have already delivered him letters, and there\n\ +appears much joy in him; even so much that joy could\n\ +not show itself modest enough without a badge of\n\ +bitterness.\n\ +LEONATO\n\ +Did he break out into tears?\n\ +MESSENGER\n\ +In great measure.\n\ +LEONATO\n\ +A kind overflow of kindness: there are no faces\n\ +truer than those that are so washed. How much\n\ +better is it to weep at joy than to joy at weeping!\n\ +BEATRICE\n\ +I pray you, is Signior Mountanto returned from the\n\ +wars or no?\n\ +MESSENGER\n\ +I know none of that name, lady: there was none such\n\ +in the army of any sort.\n\ +LEONATO\n\ +What is he that you ask for, niece?\n\ +HERO\n\ +My cousin means Signior Benedick of Padua.\n\ +MESSENGER\n\ +O, he's returned; and as pleasant as ever he was.\n\ +BEATRICE\n\ +He set up his bills here in Messina and challenged\n\ +Cupid at the flight; and my uncle's fool, reading\n\ +the challenge, subscribed for Cupid, and challenged\n\ +him at the bird-bolt. I pray you, how many hath he\n\ +killed and eaten in these wars? But how many hath\n\ +he killed? for indeed I promised to eat all of his killing.\n\ +LEONATO\n\ +Faith, niece, you tax Signior Benedick too much;\n\ +but he'll be meet with you, I doubt it not.\n\ +MESSENGER\n\ +He hath done good service, lady, in these wars.\n\ +BEATRICE\n\ +You had musty victual, and he hath holp to eat it:\n\ +he is a very valiant trencherman; he hath an\n\ +excellent stomach.\n\ +MESSENGER\n\ +And a good soldier too, lady.\n\ +BEATRICE\n\ +And a good soldier to a lady: but what is he to a lord?\n\ +MESSENGER\n\ +A lord to a lord, a man to a man; stuffed with all\n\ +honourable virtues.\n\ +BEATRICE\n\ +It is so, indeed; he is no less than a stuffed man:\n\ +but for the stuffing,--well, we are all mortal.\n\ +LEONATO\n\ +You must not, sir, mistake my niece. There is a\n\ +kind of merry war betwixt Signior Benedick and her:\n\ +they never meet but there's a skirmish of wit\n\ +between them.\n\ +BEATRICE\n\ +Alas! he gets nothing by that. In our last\n\ +conflict four of his five wits went halting off, and\n\ +now is the whole man governed with one: so that if\n\ +he have wit enough to keep himself warm, let him\n\ +bear it for a difference between himself and his\n\ +horse; for it is all the wealth that he hath left,\n\ +to be known a reasonable creature. Who is his\n\ +companion now? He hath every month a new sworn brother.\n\ +MESSENGER\n\ +Is't possible?\n\ +BEATRICE\n\ +Very easily possible: he wears his faith but as\n\ +the fashion of his hat; it ever changes with the\n\ +next block.\n\ +MESSENGER\n\ +I see, lady, the gentleman is not in your books.\n\ +BEATRICE\n\ +No; an he were, I would burn my study. But, I pray\n\ +you, who is his companion? Is there no young\n\ +squarer now that will make a voyage with him to the devil?\n\ +MESSENGER\n\ +He is most in the company of the right noble Claudio.\n\ +BEATRICE\n\ +O Lord, he will hang upon him like a disease: he\n\ +is sooner caught than the pestilence, and the taker\n\ +runs presently mad. God help the noble Claudio! if\n\ +he have caught the Benedick, it will cost him a\n\ +thousand pound ere a' be cured.\n\ +MESSENGER\n\ +I will hold friends with you, lady.\n\ +BEATRICE\n\ +Do, good friend.\n\ +LEONATO\n\ +You will never run mad, niece.\n\ +BEATRICE\n\ +No, not till a hot January.\n\ +MESSENGER\n\ +Don Pedro is approached.\n\ +Enter DON PEDRO, DON JOHN, CLAUDIO, BENEDICK, and BALTHASAR\n\ +\n\ +DON PEDRO\n\ +Good Signior Leonato, you are come to meet your\n\ +trouble: the fashion of the world is to avoid\n\ +cost, and you encounter it\n\ +LEONATO\n\ +Never came trouble to my house in the likeness"; + +TEST(liblog, max_payload) { + pid_t pid = getpid(); + char tag[sizeof(max_payload_tag)]; + memcpy(tag, max_payload_tag, sizeof(tag)); + snprintf(tag + sizeof(tag) - 5, 5, "%04X", pid & 0xFFFF); + + LOG_FAILURE_RETRY(__android_log_buf_write(LOG_ID_SYSTEM, ANDROID_LOG_INFO, + tag, max_payload_buf)); + + struct logger_list *logger_list; + + ASSERT_TRUE(NULL != (logger_list = android_logger_list_open( + LOG_ID_SYSTEM, O_RDONLY, 100, 0))); + + bool matches = false; + ssize_t max_len = 0; + + for(;;) { + log_msg log_msg; + if (android_logger_list_read(logger_list, &log_msg) <= 0) { + break; + } + + if ((log_msg.entry.pid != pid) || (log_msg.id() != LOG_ID_SYSTEM)) { + continue; + } + + char *data = log_msg.msg() + 1; + + if (strcmp(data, tag)) { + continue; + } + + data += strlen(data) + 1; + + const char *left = data; + const char *right = max_payload_buf; + while (*left && *right && (*left == *right)) { + ++left; + ++right; + } + + if (max_len <= (left - data)) { + max_len = left - data + 1; + } + + if (max_len > 512) { + matches = true; + break; + } + } + + android_logger_list_close(logger_list); + + EXPECT_EQ(true, matches); + + EXPECT_LE(sizeof(max_payload_buf), static_cast<size_t>(max_len)); +} + +TEST(liblog, too_big_payload) { + pid_t pid = getpid(); + static const char big_payload_tag[] = "TEST_big_payload_XXXX"; + char tag[sizeof(big_payload_tag)]; + memcpy(tag, big_payload_tag, sizeof(tag)); + snprintf(tag + sizeof(tag) - 5, 5, "%04X", pid & 0xFFFF); + + std::string longString(3266519, 'x'); + + ssize_t ret = LOG_FAILURE_RETRY(__android_log_buf_write(LOG_ID_SYSTEM, + ANDROID_LOG_INFO, tag, longString.c_str())); + + struct logger_list *logger_list; + + ASSERT_TRUE(NULL != (logger_list = android_logger_list_open( + LOG_ID_SYSTEM, O_RDONLY | O_NDELAY, 100, 0))); + + ssize_t max_len = 0; + + for(;;) { + log_msg log_msg; + if (android_logger_list_read(logger_list, &log_msg) <= 0) { + break; + } + + if ((log_msg.entry.pid != pid) || (log_msg.id() != LOG_ID_SYSTEM)) { + continue; + } + + char *data = log_msg.msg() + 1; + + if (strcmp(data, tag)) { + continue; + } + + data += strlen(data) + 1; + + const char *left = data; + const char *right = longString.c_str(); + while (*left && *right && (*left == *right)) { + ++left; + ++right; + } + + if (max_len <= (left - data)) { + max_len = left - data + 1; + } + } + + android_logger_list_close(logger_list); + + EXPECT_LE(LOGGER_ENTRY_MAX_PAYLOAD - sizeof(big_payload_tag), + static_cast<size_t>(max_len)); + + EXPECT_EQ(ret, max_len + static_cast<ssize_t>(sizeof(big_payload_tag))); +} + +TEST(liblog, dual_reader) { + struct logger_list *logger_list1; + + // >25 messages due to liblog.__android_log_buf_print__concurrentXX above. + ASSERT_TRUE(NULL != (logger_list1 = android_logger_list_open( + LOG_ID_MAIN, O_RDONLY | O_NDELAY, 25, 0))); + + struct logger_list *logger_list2; + + if (NULL == (logger_list2 = android_logger_list_open( + LOG_ID_MAIN, O_RDONLY | O_NDELAY, 15, 0))) { + android_logger_list_close(logger_list1); + ASSERT_TRUE(NULL != logger_list2); + } + + int count1 = 0; + bool done1 = false; + int count2 = 0; + bool done2 = false; + + do { + log_msg log_msg; + + if (!done1) { + if (android_logger_list_read(logger_list1, &log_msg) <= 0) { + done1 = true; + } else { + ++count1; + } + } + + if (!done2) { + if (android_logger_list_read(logger_list2, &log_msg) <= 0) { + done2 = true; + } else { + ++count2; + } + } + } while ((!done1) || (!done2)); + + android_logger_list_close(logger_list1); + android_logger_list_close(logger_list2); + + EXPECT_EQ(25, count1); + EXPECT_EQ(15, count2); } TEST(liblog, android_logger_get_) { @@ -308,12 +604,81 @@ TEST(liblog, android_logger_get_) { continue; } struct logger * logger; - ASSERT_EQ(0, NULL == (logger = android_logger_open(logger_list, id))); - ASSERT_EQ(id, android_logger_get_id(logger)); - ASSERT_LT(0, android_logger_get_log_size(logger)); - ASSERT_LT(0, android_logger_get_log_readable_size(logger)); - ASSERT_LT(0, android_logger_get_log_version(logger)); + EXPECT_TRUE(NULL != (logger = android_logger_open(logger_list, id))); + EXPECT_EQ(id, android_logger_get_id(logger)); + EXPECT_LT(0, android_logger_get_log_size(logger)); + EXPECT_LT(0, android_logger_get_log_readable_size(logger)); + EXPECT_LT(0, android_logger_get_log_version(logger)); } android_logger_list_close(logger_list); } + +static bool checkPriForTag(AndroidLogFormat *p_format, const char *tag, android_LogPriority pri) { + return android_log_shouldPrintLine(p_format, tag, pri) + && !android_log_shouldPrintLine(p_format, tag, (android_LogPriority)(pri - 1)); +} + +TEST(liblog, filterRule) { + static const char tag[] = "random"; + + AndroidLogFormat *p_format = android_log_format_new(); + + android_log_addFilterRule(p_format,"*:i"); + + EXPECT_TRUE(checkPriForTag(p_format, tag, ANDROID_LOG_INFO)); + EXPECT_TRUE(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0); + android_log_addFilterRule(p_format, "*"); + EXPECT_TRUE (checkPriForTag(p_format, tag, ANDROID_LOG_DEBUG)); + EXPECT_TRUE(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0); + android_log_addFilterRule(p_format, "*:v"); + EXPECT_TRUE (checkPriForTag(p_format, tag, ANDROID_LOG_VERBOSE)); + EXPECT_TRUE(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0); + android_log_addFilterRule(p_format, "*:i"); + EXPECT_TRUE (checkPriForTag(p_format, tag, ANDROID_LOG_INFO)); + EXPECT_TRUE(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0); + + android_log_addFilterRule(p_format, tag); + EXPECT_TRUE (checkPriForTag(p_format, tag, ANDROID_LOG_VERBOSE)); + EXPECT_TRUE(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0); + android_log_addFilterRule(p_format, "random:v"); + EXPECT_TRUE (checkPriForTag(p_format, tag, ANDROID_LOG_VERBOSE)); + EXPECT_TRUE(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0); + android_log_addFilterRule(p_format, "random:d"); + EXPECT_TRUE (checkPriForTag(p_format, tag, ANDROID_LOG_DEBUG)); + EXPECT_TRUE(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0); + android_log_addFilterRule(p_format, "random:w"); + EXPECT_TRUE (checkPriForTag(p_format, tag, ANDROID_LOG_WARN)); + EXPECT_TRUE(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0); + + android_log_addFilterRule(p_format, "crap:*"); + EXPECT_TRUE (checkPriForTag(p_format, "crap", ANDROID_LOG_VERBOSE)); + EXPECT_TRUE(android_log_shouldPrintLine(p_format, "crap", ANDROID_LOG_VERBOSE) > 0); + + // invalid expression + EXPECT_TRUE (android_log_addFilterRule(p_format, "random:z") < 0); + EXPECT_TRUE (checkPriForTag(p_format, tag, ANDROID_LOG_WARN)); + EXPECT_TRUE(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0); + + // Issue #550946 + EXPECT_TRUE(android_log_addFilterString(p_format, " ") == 0); + EXPECT_TRUE(checkPriForTag(p_format, tag, ANDROID_LOG_WARN)); + + // note trailing space + EXPECT_TRUE(android_log_addFilterString(p_format, "*:s random:d ") == 0); + EXPECT_TRUE(checkPriForTag(p_format, tag, ANDROID_LOG_DEBUG)); + + EXPECT_TRUE(android_log_addFilterString(p_format, "*:s random:z") < 0); + +#if 0 // bitrot, seek update + char defaultBuffer[512]; + + android_log_formatLogLine(p_format, + defaultBuffer, sizeof(defaultBuffer), 0, ANDROID_LOG_ERROR, 123, + 123, 123, tag, "nofile", strlen("Hello"), "Hello", NULL); + + fprintf(stderr, "%s\n", defaultBuffer); +#endif + + android_log_format_free(p_format); +} diff --git a/libmincrypt/dsa_sig.c b/libmincrypt/dsa_sig.c index 8df6cf7..101314b 100644 --- a/libmincrypt/dsa_sig.c +++ b/libmincrypt/dsa_sig.c @@ -26,6 +26,7 @@ #include <string.h> +#include "mincrypt/dsa_sig.h" #include "mincrypt/p256.h" /** diff --git a/libmincrypt/p256.c b/libmincrypt/p256.c index 1608d37..555a07a 100644 --- a/libmincrypt/p256.c +++ b/libmincrypt/p256.c @@ -49,8 +49,6 @@ const p256_int SECP256r1_b = // curve b {{0x27d2604b, 0x3bce3c3e, 0xcc53b0f6, 0x651d06b0, 0x769886bc, 0xb3ebbd55, 0xaa3a93e7, 0x5ac635d8}}; -static const p256_int p256_one = P256_ONE; - void p256_init(p256_int* a) { memset(a, 0, sizeof(*a)); } diff --git a/libmincrypt/test/ecdsa_test.c b/libmincrypt/test/ecdsa_test.c index b5a7b3a..24ec013 100644 --- a/libmincrypt/test/ecdsa_test.c +++ b/libmincrypt/test/ecdsa_test.c @@ -24,15 +24,21 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include <stdio.h> #include <ctype.h> +#include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sys/cdefs.h> +#include "mincrypt/dsa_sig.h" #include "mincrypt/p256.h" #include "mincrypt/p256_ecdsa.h" #include "mincrypt/sha256.h" +#ifndef __unused +#define __unused __attribute__((__unused__)) +#endif + /** * Messages signed using: * @@ -209,7 +215,7 @@ unsigned char* parsehex(char* str, int* len) { return result; } -int main(int arg, char** argv) { +int main(int arg __unused, char** argv __unused) { unsigned char hash_buf[SHA256_DIGEST_SIZE]; diff --git a/libmincrypt/test/rsa_test.c b/libmincrypt/test/rsa_test.c index 17862dc..055138f 100644 --- a/libmincrypt/test/rsa_test.c +++ b/libmincrypt/test/rsa_test.c @@ -1,5 +1,4 @@ -/* rsa_test.c -** +/* ** Copyright 2013, The Android Open Source Project ** ** Redistribution and use in source and binary forms, with or without @@ -25,14 +24,19 @@ ** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include <stdio.h> #include <ctype.h> +#include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sys/cdefs.h> #include "mincrypt/rsa.h" #include "mincrypt/sha.h" +#ifndef __unused +#define __unused __attribute__((unused)) +#endif + // RSA test data taken from: // // ftp://ftp.rsa.com/pub/rsalabs/tmp/pkcs1v15sign-vectors.txt @@ -791,7 +795,7 @@ unsigned char* parsehex(char* str, int* len) { } -int main(int arg, char** argv) { +int main(int arg __unused, char** argv __unused) { unsigned char hash[SHA_DIGEST_SIZE]; diff --git a/libnetutils/Android.mk b/libnetutils/Android.mk index aba4621..1f61511 100644 --- a/libnetutils/Android.mk +++ b/libnetutils/Android.mk @@ -1,7 +1,7 @@ -LOCAL_PATH:= $(call my-dir) +LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -LOCAL_SRC_FILES:= \ +LOCAL_SRC_FILES := \ dhcpclient.c \ dhcpmsg.c \ dhcp_utils.c \ @@ -12,6 +12,8 @@ LOCAL_SHARED_LIBRARIES := \ libcutils \ liblog -LOCAL_MODULE:= libnetutils +LOCAL_MODULE := libnetutils + +LOCAL_CFLAGS := -Werror include $(BUILD_SHARED_LIBRARY) diff --git a/libnetutils/dhcpclient.c b/libnetutils/dhcpclient.c index 34500e7..b58120e 100644 --- a/libnetutils/dhcpclient.c +++ b/libnetutils/dhcpclient.c @@ -282,16 +282,18 @@ void dump_dhcp_msg(dhcp_msg *msg, int len) ALOGD("chaddr = {%s}", buf); for (n = 0; n < 64; n++) { - if ((msg->sname[n] < ' ') || (msg->sname[n] > 127)) { - if (msg->sname[n] == 0) break; + unsigned char x = msg->sname[n]; + if ((x < ' ') || (x > 127)) { + if (x == 0) break; msg->sname[n] = '.'; } } msg->sname[63] = 0; for (n = 0; n < 128; n++) { - if ((msg->file[n] < ' ') || (msg->file[n] > 127)) { - if (msg->file[n] == 0) break; + unsigned char x = msg->file[n]; + if ((x < ' ') || (x > 127)) { + if (x == 0) break; msg->file[n] = '.'; } } diff --git a/libnl_2/.gitignore b/libnl_2/.gitignore deleted file mode 100644 index d4ca744..0000000 --- a/libnl_2/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -include/netlink/version.h.in -cscope.* diff --git a/libnl_2/Android.mk b/libnl_2/Android.mk deleted file mode 100644 index deac9de..0000000 --- a/libnl_2/Android.mk +++ /dev/null @@ -1,37 +0,0 @@ -####################################### -# * Netlink cache not implemented -# * Library is not thread safe -####################################### - -LOCAL_PATH := $(call my-dir) - - -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := \ - attr.c \ - cache.c \ - genl/genl.c \ - genl/family.c \ - handlers.c \ - msg.c \ - netlink.c \ - object.c \ - socket.c \ - dbg.c - -LOCAL_C_INCLUDES += \ - external/libnl-headers - -# Static Library -LOCAL_MODULE := libnl_2 -LOCAL_MODULE_TAGS := optional -include $(BUILD_STATIC_LIBRARY) - -include $(CLEAR_VARS) -LOCAL_SRC_FILES := -LOCAL_WHOLE_STATIC_LIBRARIES:= libnl_2 -LOCAL_SHARED_LIBRARIES:= liblog -LOCAL_MODULE := libnl_2 -LOCAL_MODULE_TAGS := optional -include $(BUILD_SHARED_LIBRARY) diff --git a/libnl_2/README b/libnl_2/README deleted file mode 100644 index 14db6db..0000000 --- a/libnl_2/README +++ /dev/null @@ -1,88 +0,0 @@ -Netlink Protocol Library - -This library is a clean room re-implementation of libnl 2.0 and -re-licensed under Apache 2.0. It was developed primarily to support -wpa_supplicant. However, with additional development can be extended -to support other netlink applications. - -Netlink Protocol Format (RFC3549) - -+-----------------+-+-------------------+-+ -|Netlink Message |P| Generic Netlink |P| -| Header |A| Message Header |A| -|(struct nlmsghdr)|D|(struct genlmsghdr)|D| -+-----------------+-+-------------------+-+-------------+ -|len:4|type:2|flags:2|seq:4 pid:4|cmd:1|ver:1|reserved:2| -+--------------------------------+----------------------+ -+-----------------+-+-----------------+-+-----------------+-+-----------------+-+---+ -|Netlink Attribute|P|Netlink Attribute|P|Netlink Attribute|P|Netlink Attribute|P|...| -| #0 Header |A| #0 Payload |A| #1 Header |A| #1 Payload |A| | -| (struct nlattr) |D| (void) |D| (struct nlattr) |D| (void) |D| | -+-----------------+-+-----------------+-+-----------------+-+-----------------+-+---+ -|len:2(==4+payload)|type:2|payload|pad| -+-------------------------+-------+---+ - -NETLINK OVERVIEW - -* Each netlink message consists of a bitstream with a netlink header. -* After this header a second header *can* be used specific to the netlink - family in use. This library was tested using the generic netlink - protocol defined by struct genlmsghdr to support nl80211. -* After the header(s) netlink attributes can be appended to the message - which hold can hold basic types such as unsigned integers and strings. -* Attributes can also be nested. This is accomplished by calling "nla_nest_start" - which creates an empty attribute with nest attributes as its payload. Then to - close the nest, "nla_nest_end" is called. -* All data structures in this implementation are byte-aligned (Currently 4 bytes). -* Acknowledgements (ACKs) are sent as NLMSG_ERROR netlink message types (0x2) and - have an error value of 0. - -KNOWN ISSUES - - GENERAL - * Not tested for thread safety - - Android.mk - * No dynamic library because of netlink cache not implemented and - not tested for thread safety - - attr.c - * nla_parse - does not use nla_policy argument - - cache.c - * netlink cache not implemented and only supports one netlink family id - which is stored in the nl_cache pointer instead of an actual cache - - netlink.c - * nl_recvmsgs - does not support nl_cb_overwrite_recv() - * nl_recv - sets/unsets asynchronous socket flag - -SOURCE FILES - -* Android.mk - Android makefile -* README - This file -* attr.c - Netlink attributes -* cache.c - Netlink cache -* genl/family.c - Generic netlink family id -* genl/genl.c - Generic netlink -* handlers.c - Netlink callbacks -* msg.c - Netlink messages construction -* netlink.c - Netlink socket communication -* object.c - libnl object wrapper -* socket.c - Netlink kernel socket utils - -IMPORTANT HEADER FILES - NOTE: These are based on the the origin GPL libnl headers - -* netlink-types.h - Contains many important structs for libnl - to represent netlink objects -* netlink/netlink-kernel.h - Netlink kernel headers and field constants. -* netlink/msg.h - macros for iterating over netlink messages -* netlink/attr.h - netlink attribute constants, iteration macros and setters - -REFERENCES - -* nl80211.h -* netlink_types.h -* $LINUX_KERNEL/net/wireless/nl80211.c -* http://www.infradead.org/~tgr/libnl/doc-3.0/index.html -* http://www.netfilter.org/projects/libmnl/doxygen/index.html diff --git a/libnl_2/attr.c b/libnl_2/attr.c deleted file mode 100644 index 2ef7590..0000000 --- a/libnl_2/attr.c +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -/* NOTICE: This is a clean room re-implementation of libnl */ - -#include <errno.h> -#include "netlink/netlink.h" -#include "netlink/msg.h" -#include "netlink/attr.h" -#include "netlink-types.h" - -/* Return payload of string attribute. */ -char *nla_get_string(struct nlattr *nla) -{ - return (char *) nla_data(nla); -} - -/* Return payload of 16 bit integer attribute. */ -uint16_t nla_get_u16(struct nlattr *nla) -{ - return *((uint16_t *) nla_data(nla)); -} - -/* Return payload of 32 bit integer attribute. */ -uint32_t nla_get_u32(struct nlattr *nla) -{ - return *((uint32_t *) nla_data(nla)); -} - -/* Return value of 8 bit integer attribute. */ -uint8_t nla_get_u8(struct nlattr *nla) -{ - return *((uint8_t *) nla_data(nla)); -} - -/* Return payload of uint64_t attribute. */ -uint64_t nla_get_u64(struct nlattr *nla) -{ - uint64_t tmp; - nla_memcpy(&tmp, nla, sizeof(tmp)); - return tmp; -} - -/* Head of payload */ -void *nla_data(const struct nlattr *nla) -{ - return (void *) ((char *) nla + NLA_HDRLEN); -} - -/* Return length of the payload . */ -int nla_len(const struct nlattr *nla) -{ - return nla->nla_len - NLA_HDRLEN; -} - -int nla_padlen(int payload) -{ - return NLA_ALIGN(payload) - payload; -} - -/* Start a new level of nested attributes. */ -struct nlattr *nla_nest_start(struct nl_msg *msg, int attrtype) -{ - struct nlattr *start = (struct nlattr *)nlmsg_tail(msg->nm_nlh); - int rc; - - rc = nla_put(msg, attrtype, 0, NULL); - if (rc < 0) - return NULL; - - return start; -} - -/* Finalize nesting of attributes. */ -int nla_nest_end(struct nl_msg *msg, struct nlattr *start) -{ - /* Set attribute size */ - start->nla_len = (unsigned char *)nlmsg_tail(nlmsg_hdr(msg)) - - (unsigned char *)start; - return 0; -} - -/* Return next attribute in a stream of attributes. */ -struct nlattr *nla_next(const struct nlattr *nla, int *remaining) -{ - struct nlattr *next_nla = NULL; - if (nla->nla_len >= sizeof(struct nlattr) && - nla->nla_len <= *remaining){ - next_nla = (struct nlattr *) \ - ((char *) nla + NLA_ALIGN(nla->nla_len)); - *remaining = *remaining - NLA_ALIGN(nla->nla_len); - } - - return next_nla; - -} - -/* Check if the attribute header and payload can be accessed safely. */ -int nla_ok(const struct nlattr *nla, int remaining) -{ - return remaining > 0 && - nla->nla_len >= sizeof(struct nlattr) && - sizeof(struct nlattr) <= (unsigned int) remaining && - nla->nla_len <= remaining; -} - -/* Create attribute index based on a stream of attributes. */ -/* NOTE: Policy not used ! */ -int nla_parse(struct nlattr *tb[], int maxtype, struct nlattr *head, - int len, struct nla_policy *policy) -{ - struct nlattr *pos; - int rem; - - /* First clear table */ - memset(tb, 0, (maxtype + 1) * sizeof(struct nlattr *)); - - nla_for_each_attr(pos, head, len, rem) { - int type = nla_type(pos); - - if ((type <= maxtype) && (type != 0)) - tb[type] = pos; - } - - return 0; -} - - -/* Create attribute index based on nested attribute. */ -int nla_parse_nested(struct nlattr *tb[], int maxtype, - struct nlattr *nla, struct nla_policy *policy) -{ - return nla_parse(tb, maxtype, nla_data(nla), nla_len(nla), policy); -} - - -/* Add a unspecific attribute to netlink message. */ -int nla_put(struct nl_msg *msg, int attrtype, int datalen, const void *data) -{ - struct nlattr *nla; - - /* Reserve space and init nla header */ - nla = nla_reserve(msg, attrtype, datalen); - if (nla) { - memcpy(nla_data(nla), data, datalen); - return 0; - } - - return -EINVAL; -} - -/* Add 8 bit integer attribute to netlink message. */ -int nla_put_u8(struct nl_msg *msg, int attrtype, uint8_t value) -{ - return nla_put(msg, attrtype, sizeof(uint8_t), &value); -} - -/* Add 16 bit integer attribute to netlink message. */ -int nla_put_u16(struct nl_msg *msg, int attrtype, uint16_t value) -{ - return nla_put(msg, attrtype, sizeof(uint16_t), &value); -} - -/* Add 32 bit integer attribute to netlink message. */ -int nla_put_u32(struct nl_msg *msg, int attrtype, uint32_t value) -{ - return nla_put(msg, attrtype, sizeof(uint32_t), &value); -} - -/* Add 64 bit integer attribute to netlink message. */ -int nla_put_u64(struct nl_msg *msg, int attrtype, uint64_t value) -{ - return nla_put(msg, attrtype, sizeof(uint64_t), &value); -} - -/* Add nested attributes to netlink message. */ -/* Takes the attributes found in the nested message and appends them - * to the message msg nested in a container of the type attrtype. The - * nested message may not have a family specific header */ -int nla_put_nested(struct nl_msg *msg, int attrtype, struct nl_msg *nested) -{ - int rc; - - rc = nla_put(msg, attrtype, nlmsg_attrlen(nlmsg_hdr(nested), 0), - nlmsg_attrdata(nlmsg_hdr(nested), 0)); - return rc; - -} - -/* Return type of the attribute. */ -int nla_type(const struct nlattr *nla) -{ - return (int)nla->nla_type & NLA_TYPE_MASK; -} - -/* Reserves room for an attribute in specified netlink message and fills - * in the attribute header (type,length). Return NULL if insufficient space */ -struct nlattr *nla_reserve(struct nl_msg *msg, int attrtype, int data_len) -{ - - struct nlattr *nla; - const unsigned int NEW_SIZE = NLMSG_ALIGN(msg->nm_nlh->nlmsg_len) + - NLA_ALIGN(NLA_HDRLEN + data_len); - - /* Check enough space for attribute */ - if (NEW_SIZE > msg->nm_size) - return NULL; - - nla = (struct nlattr *)nlmsg_tail(msg->nm_nlh); - nla->nla_type = attrtype; - nla->nla_len = NLA_HDRLEN + data_len; - memset((unsigned char *)nla + nla->nla_len, 0, nla_padlen(data_len)); - msg->nm_nlh->nlmsg_len = NEW_SIZE; - return nla; -} - -/* Copy attribute payload to another memory area. */ -int nla_memcpy(void *dest, struct nlattr *src, int count) -{ - if (!src || !dest) - return 0; - if (count > nla_len(src)) - count = nla_len(src); - memcpy(dest, nla_data(src), count); - return count; -} diff --git a/libnl_2/dbg.c b/libnl_2/dbg.c deleted file mode 100644 index 9764de6..0000000 --- a/libnl_2/dbg.c +++ /dev/null @@ -1,12 +0,0 @@ -#include "netlink/netlink.h" -#include <android/log.h> - -void libnl_printf(int level, char *format, ...) -{ - va_list ap; - - level = ANDROID_LOG_ERROR; - va_start(ap, format); - __android_log_vprint(level, "libnl_2", format, ap); - va_end(ap); -} diff --git a/libnl_2/genl/family.c b/libnl_2/genl/family.c deleted file mode 100644 index 1beee6e..0000000 --- a/libnl_2/genl/family.c +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -/* NOTICE: This is a clean room re-implementation of libnl */ - -#include "netlink-types.h" - -static struct genl_family *genl_family_find_byname(const char *name) -{ - return NULL; -} - -/* Release reference and none outstanding */ -void genl_family_put(struct genl_family *family) -{ - family->ce_refcnt--; - if (family->ce_refcnt <= 0) - free(family); -} - -unsigned int genl_family_get_id(struct genl_family *family) -{ - const int NO_FAMILY_ID = 0; - - if (!family) - return NO_FAMILY_ID; - else - return family->gf_id; - -} - diff --git a/libnl_2/genl/genl.c b/libnl_2/genl/genl.c deleted file mode 100644 index 1a39c6a..0000000 --- a/libnl_2/genl/genl.c +++ /dev/null @@ -1,302 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -/* NOTICE: This is a clean room re-implementation of libnl */ - -#include <errno.h> -#include <unistd.h> -#include <stdio.h> -#include <sys/time.h> -#include <sys/socket.h> -#include <linux/netlink.h> -#include <netlink/genl/ctrl.h> -#include <netlink/genl/family.h> -#include "netlink-types.h" - -/* Get head of attribute data. */ -struct nlattr *genlmsg_attrdata(const struct genlmsghdr *gnlh, int hdrlen) -{ - return (struct nlattr *) \ - ((char *) gnlh + GENL_HDRLEN + NLMSG_ALIGN(hdrlen)); - -} - -/* Get length of attribute data. */ -int genlmsg_attrlen(const struct genlmsghdr *gnlh, int hdrlen) -{ - struct nlattr *nla; - struct nlmsghdr *nlh; - - nla = genlmsg_attrdata(gnlh, hdrlen); - nlh = (struct nlmsghdr *) ((char *) gnlh - NLMSG_HDRLEN); - return (char *) nlmsg_tail(nlh) - (char *) nla; -} - -/* Add generic netlink header to netlink message. */ -void *genlmsg_put(struct nl_msg *msg, uint32_t pid, uint32_t seq, int family, - int hdrlen, int flags, uint8_t cmd, uint8_t version) -{ - int new_size; - struct nlmsghdr *nlh; - struct timeval tv; - struct genlmsghdr *gmh; - - /* Make sure nl_msg has enough space */ - new_size = NLMSG_HDRLEN + GENL_HDRLEN + hdrlen; - if ((sizeof(struct nl_msg) + new_size) > msg->nm_size) - goto fail; - - /* Fill in netlink header */ - nlh = msg->nm_nlh; - nlh->nlmsg_len = new_size; - nlh->nlmsg_type = family; - nlh->nlmsg_pid = getpid(); - nlh->nlmsg_flags = flags | NLM_F_REQUEST | NLM_F_ACK; - - /* Get current time for sequence number */ - if (gettimeofday(&tv, NULL)) - nlh->nlmsg_seq = 1; - else - nlh->nlmsg_seq = (int) tv.tv_sec; - - /* Setup genlmsghdr in new message */ - gmh = (struct genlmsghdr *) ((char *)nlh + NLMSG_HDRLEN); - gmh->cmd = (__u8) cmd; - gmh->version = version; - - return gmh; -fail: - return NULL; - -} - -/* Socket has already been alloced to connect it to kernel? */ -int genl_connect(struct nl_sock *sk) -{ - return nl_connect(sk, NETLINK_GENERIC); - -} - -int genl_ctrl_alloc_cache(struct nl_sock *sock, struct nl_cache **result) -{ - int rc = -1; - int nl80211_genl_id = -1; - char sendbuf[sizeof(struct nlmsghdr)+sizeof(struct genlmsghdr)]; - struct nlmsghdr nlmhdr; - struct genlmsghdr gmhhdr; - struct iovec sendmsg_iov; - struct msghdr msg; - int num_char; - const int RECV_BUF_SIZE = getpagesize(); - char *recvbuf; - struct iovec recvmsg_iov; - int nl80211_flag = 0, nlm_f_multi = 0, nlmsg_done = 0; - struct nlmsghdr *nlh; - - /* REQUEST GENERIC NETLINK FAMILY ID */ - /* Message buffer */ - nlmhdr.nlmsg_len = sizeof(sendbuf); - nlmhdr.nlmsg_type = NETLINK_GENERIC; - nlmhdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP; - nlmhdr.nlmsg_seq = sock->s_seq_next; - nlmhdr.nlmsg_pid = sock->s_local.nl_pid; - - /* Generic netlink header */ - memset(&gmhhdr, 0, sizeof(gmhhdr)); - gmhhdr.cmd = CTRL_CMD_GETFAMILY; - gmhhdr.version = CTRL_ATTR_FAMILY_ID; - - /* Combine netlink and generic netlink headers */ - memcpy(&sendbuf[0], &nlmhdr, sizeof(nlmhdr)); - memcpy(&sendbuf[0]+sizeof(nlmhdr), &gmhhdr, sizeof(gmhhdr)); - - /* Create IO vector with Netlink message */ - sendmsg_iov.iov_base = &sendbuf; - sendmsg_iov.iov_len = sizeof(sendbuf); - - /* Socket message */ - msg.msg_name = (void *) &sock->s_peer; - msg.msg_namelen = sizeof(sock->s_peer); - msg.msg_iov = &sendmsg_iov; - msg.msg_iovlen = 1; /* Only sending one iov */ - msg.msg_control = NULL; - msg.msg_controllen = 0; - msg.msg_flags = 0; - - /* Send message and verify sent */ - num_char = sendmsg(sock->s_fd, &msg, 0); - if (num_char == -1) - return -errno; - - /* RECEIVE GENL CMD RESPONSE */ - - /* Create receive iov buffer */ - recvbuf = (char *) malloc(RECV_BUF_SIZE); - - /* Attach to iov */ - recvmsg_iov.iov_base = recvbuf; - recvmsg_iov.iov_len = RECV_BUF_SIZE; - - msg.msg_iov = &recvmsg_iov; - msg.msg_iovlen = 1; - - /***************************************************************/ - /* Receive message. If multipart message, keep receiving until */ - /* message type is NLMSG_DONE */ - /***************************************************************/ - - do { - - int recvmsg_len, nlmsg_rem; - - /* Receive message */ - memset(recvbuf, 0, RECV_BUF_SIZE); - recvmsg_len = recvmsg(sock->s_fd, &msg, 0); - - /* Make sure receive successful */ - if (recvmsg_len < 0) { - rc = -errno; - goto error_recvbuf; - } - - /* Parse nlmsghdr */ - nlmsg_for_each_msg(nlh, (struct nlmsghdr *) recvbuf, \ - recvmsg_len, nlmsg_rem) { - struct nlattr *nla; - int nla_rem; - - /* Check type */ - switch (nlh->nlmsg_type) { - case NLMSG_DONE: - goto return_genl_id; - break; - case NLMSG_ERROR: - - /* Should check nlmsgerr struct received */ - fprintf(stderr, "Receive message error\n"); - goto error_recvbuf; - case NLMSG_OVERRUN: - fprintf(stderr, "Receive data partly lost\n"); - goto error_recvbuf; - case NLMSG_MIN_TYPE: - case NLMSG_NOOP: - break; - default: - break; - } - - - - /* Check flags */ - if (nlh->nlmsg_flags & NLM_F_MULTI) - nlm_f_multi = 1; - else - nlm_f_multi = 0; - - if (nlh->nlmsg_type & NLMSG_DONE) - nlmsg_done = 1; - else - nlmsg_done = 0; - - /* Iteratve over attributes */ - nla_for_each_attr(nla, - nlmsg_attrdata(nlh, GENL_HDRLEN), - nlmsg_attrlen(nlh, GENL_HDRLEN), - nla_rem){ - - /* If this family is nl80211 */ - if (nla->nla_type == CTRL_ATTR_FAMILY_NAME && - !strcmp((char *)nla_data(nla), - "nl80211")) - nl80211_flag = 1; - - /* Save the family id */ - else if (nl80211_flag && - nla->nla_type == CTRL_ATTR_FAMILY_ID) { - nl80211_genl_id = - *((int *)nla_data(nla)); - nl80211_flag = 0; - } - - } - - } - - } while (nlm_f_multi && !nlmsg_done); - -return_genl_id: - /* Return family id as cache pointer */ - *result = (struct nl_cache *) nl80211_genl_id; - rc = 0; -error_recvbuf: - free(recvbuf); -error: - return rc; -} - -/* Checks the netlink cache to find family reference by name string */ -/* NOTE: Caller needs to call genl_family_put() when done with * - * returned object */ -struct genl_family *genl_ctrl_search_by_name(struct nl_cache *cache, \ - const char *name) -{ - struct genl_family *gf = (struct genl_family *) \ - malloc(sizeof(struct genl_family)); - if (!gf) - goto fail; - memset(gf, 0, sizeof(*gf)); - - /* Add ref */ - gf->ce_refcnt++; - - /* Overriding cache pointer as family id for now */ - gf->gf_id = (uint16_t) ((uint32_t) cache); - strncpy(gf->gf_name, name, GENL_NAMSIZ); - - return gf; -fail: - return NULL; - -} - -int genl_ctrl_resolve(struct nl_sock *sk, const char *name) -{ - struct nl_cache *cache = NULL; - struct genl_family *gf = NULL; - int id = -1; - - /* Hack to support wpa_supplicant */ - if (strcmp(name, "nlctrl") == 0) - return NETLINK_GENERIC; - - if (strcmp(name, "nl80211") != 0) { - fprintf(stderr, "%s is not supported\n", name); - return id; - } - - if (!genl_ctrl_alloc_cache(sk, &cache)) { - gf = genl_ctrl_search_by_name(cache, name); - if (gf) - id = genl_family_get_id(gf); - } - - if (gf) - genl_family_put(gf); - if (cache) - nl_cache_free(cache); - - return id; -} diff --git a/libnl_2/handlers.c b/libnl_2/handlers.c deleted file mode 100644 index 48dcab4..0000000 --- a/libnl_2/handlers.c +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -/* NOTICE: This is a clean room re-implementation of libnl */ - -#include <malloc.h> -#include "netlink-types.h" -#include "netlink/handlers.h" - -/* Allocate a new callback handle. */ -struct nl_cb *nl_cb_alloc(enum nl_cb_kind kind) -{ - struct nl_cb *cb; - - cb = (struct nl_cb *) malloc(sizeof(struct nl_cb)); - if (cb == NULL) - goto fail; - memset(cb, 0, sizeof(*cb)); - - return nl_cb_get(cb); -fail: - return NULL; -} - -/* Clone an existing callback handle */ -struct nl_cb *nl_cb_clone(struct nl_cb *orig) -{ - struct nl_cb *new_cb; - - new_cb = nl_cb_alloc(NL_CB_DEFAULT); - if (new_cb == NULL) - goto fail; - - /* Copy original and set refcount to 1 */ - memcpy(new_cb, orig, sizeof(*orig)); - new_cb->cb_refcnt = 1; - - return new_cb; -fail: - return NULL; -} - -/* Set up a callback. */ -int nl_cb_set(struct nl_cb *cb, enum nl_cb_type type, enum nl_cb_kind kind, \ - nl_recvmsg_msg_cb_t func, void *arg) -{ - cb->cb_set[type] = func; - cb->cb_args[type] = arg; - return 0; -} - - - -/* Set up an error callback. */ -int nl_cb_err(struct nl_cb *cb, enum nl_cb_kind kind, \ - nl_recvmsg_err_cb_t func, void *arg) -{ - cb->cb_err = func; - cb->cb_err_arg = arg; - return 0; - -} - -struct nl_cb *nl_cb_get(struct nl_cb *cb) -{ - cb->cb_refcnt++; - return cb; -} - -void nl_cb_put(struct nl_cb *cb) -{ - if (!cb) - return; - cb->cb_refcnt--; - if (cb->cb_refcnt <= 0) - free(cb); -} diff --git a/libnl_2/msg.c b/libnl_2/msg.c deleted file mode 100644 index 1303e8a..0000000 --- a/libnl_2/msg.c +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -/* NOTICE: This is a clean room re-implementation of libnl */ - -#include <malloc.h> -#include <unistd.h> -#include <sys/socket.h> -#include <linux/netlink.h> -#include "netlink-types.h" - -/* Allocate a new netlink message with the default maximum payload size. */ -struct nl_msg *nlmsg_alloc(void) -{ - /* Whole page will store nl_msg + nlmsghdr + genlmsghdr + payload */ - const int page_sz = getpagesize(); - struct nl_msg *nm; - struct nlmsghdr *nlh; - - /* Netlink message */ - nm = (struct nl_msg *) malloc(page_sz); - if (!nm) - goto fail; - - /* Netlink message header pointer */ - nlh = (struct nlmsghdr *) ((char *) nm + sizeof(struct nl_msg)); - - /* Initialize */ - memset(nm, 0, page_sz); - nm->nm_size = page_sz; - - nm->nm_src.nl_family = AF_NETLINK; - nm->nm_src.nl_pid = getpid(); - - nm->nm_dst.nl_family = AF_NETLINK; - nm->nm_dst.nl_pid = 0; /* Kernel */ - - /* Initialize and add to netlink message */ - nlh->nlmsg_len = NLMSG_HDRLEN; - nm->nm_nlh = nlh; - - /* Add to reference count and return nl_msg */ - nlmsg_get(nm); - return nm; -fail: - return NULL; -} - -/* Return pointer to message payload. */ -void *nlmsg_data(const struct nlmsghdr *nlh) -{ - return (char *) nlh + NLMSG_HDRLEN; -} - -/* Add reference count to nl_msg */ -void nlmsg_get(struct nl_msg *nm) -{ - nm->nm_refcnt++; -} - -/* Release a reference from an netlink message. */ -void nlmsg_free(struct nl_msg *nm) -{ - if (nm) { - nm->nm_refcnt--; - if (nm->nm_refcnt <= 0) - free(nm); - } - -} - -/* Return actual netlink message. */ -struct nlmsghdr *nlmsg_hdr(struct nl_msg *n) -{ - return n->nm_nlh; -} - -/* Return head of attributes data / payload section */ -struct nlattr *nlmsg_attrdata(const struct nlmsghdr *nlh, int hdrlen) -{ - unsigned char *data = nlmsg_data(nlh); - return (struct nlattr *)(data + NLMSG_ALIGN(hdrlen)); -} - -/* Returns pointer to end of netlink message */ -void *nlmsg_tail(const struct nlmsghdr *nlh) -{ - return (void *)((char *)nlh + NLMSG_ALIGN(nlh->nlmsg_len)); -} - -/* Next netlink message in message stream */ -struct nlmsghdr *nlmsg_next(struct nlmsghdr *nlh, int *remaining) -{ - struct nlmsghdr *next_nlh = NULL; - int len = nlmsg_len(nlh); - - len = NLMSG_ALIGN(len); - if (*remaining > 0 && - len <= *remaining && - len >= (int) sizeof(struct nlmsghdr)) { - next_nlh = (struct nlmsghdr *)((char *)nlh + len); - *remaining -= len; - } - - return next_nlh; -} - -int nlmsg_datalen(const struct nlmsghdr *nlh) -{ - return nlh->nlmsg_len - NLMSG_HDRLEN; -} - -/* Length of attributes data */ -int nlmsg_attrlen(const struct nlmsghdr *nlh, int hdrlen) -{ - return nlmsg_datalen(nlh) - NLMSG_ALIGN(hdrlen); -} - -/* Length of netlink message */ -int nlmsg_len(const struct nlmsghdr *nlh) -{ - return nlh->nlmsg_len; -} - -/* Check if the netlink message fits into the remaining bytes */ -int nlmsg_ok(const struct nlmsghdr *nlh, int rem) -{ - return rem >= (int)sizeof(struct nlmsghdr) && - rem >= nlmsg_len(nlh) && - nlmsg_len(nlh) >= (int) sizeof(struct nlmsghdr) && - nlmsg_len(nlh) <= (rem); -} - -int nlmsg_padlen(int payload) -{ - return NLMSG_ALIGN(payload) - payload; -} diff --git a/libnl_2/netlink.c b/libnl_2/netlink.c deleted file mode 100644 index ee3d600..0000000 --- a/libnl_2/netlink.c +++ /dev/null @@ -1,273 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -/* NOTICE: This is a clean room re-implementation of libnl */ - -#include <errno.h> -#include <string.h> -#include <unistd.h> -#include <fcntl.h> -#include <sys/socket.h> -#include "netlink-types.h" - -#define NL_BUFFER_SZ (32768U) - -/* Checks message for completeness and sends it out */ -int nl_send_auto_complete(struct nl_sock *sk, struct nl_msg *msg) -{ - struct nlmsghdr *nlh = msg->nm_nlh; - struct timeval tv; - - if (!nlh) { - int errsv = errno; - fprintf(stderr, "Netlink message header is NULL!\n"); - return -errsv; - } - - /* Complete the nl_msg header */ - if (gettimeofday(&tv, NULL)) - nlh->nlmsg_seq = 1; - else - nlh->nlmsg_seq = (int) tv.tv_sec; - nlh->nlmsg_pid = sk->s_local.nl_pid; - nlh->nlmsg_flags |= NLM_F_REQUEST | NLM_F_ACK; - - return nl_send(sk, msg); -} - -/* Receives a netlink message, allocates a buffer in *buf and stores - * the message content. The peer's netlink address is stored in - * *nla. The caller is responsible for freeing the buffer allocated in - * *buf if a positive value is returned. Interrupted system calls are - * handled by repeating the read. The input buffer size is determined - * by peeking before the actual read is done */ -int nl_recv(struct nl_sock *sk, struct sockaddr_nl *nla, \ - unsigned char **buf, struct ucred **creds) -{ - int rc = -1; - int sk_flags; - int RECV_BUF_SIZE = getpagesize(); - int errsv; - struct iovec recvmsg_iov; - struct msghdr msg; - - /* Allocate buffer */ - *buf = (unsigned char *) malloc(RECV_BUF_SIZE); - if (!(*buf)) { - rc = -ENOMEM; - goto fail; - } - - /* Prepare to receive message */ - recvmsg_iov.iov_base = *buf; - recvmsg_iov.iov_len = RECV_BUF_SIZE; - - msg.msg_name = (void *) &sk->s_peer; - msg.msg_namelen = sizeof(sk->s_peer); - msg.msg_iov = &recvmsg_iov; - msg.msg_iovlen = 1; - msg.msg_control = NULL; - msg.msg_controllen = 0; - msg.msg_flags = 0; - - /* Make non blocking and then restore previous setting */ - sk_flags = fcntl(sk->s_fd, F_GETFL, 0); - fcntl(sk->s_fd, F_SETFL, O_NONBLOCK); - rc = recvmsg(sk->s_fd, &msg, 0); - errsv = errno; - fcntl(sk->s_fd, F_SETFL, sk_flags); - - if (rc < 0) { - rc = -errsv; - free(*buf); - *buf = NULL; - } - -fail: - return rc; -} - -/* Receive a set of messages from a netlink socket */ -/* NOTE: Does not currently support callback replacements!!! */ -int nl_recvmsgs(struct nl_sock *sk, struct nl_cb *cb) -{ - struct sockaddr_nl nla; - struct ucred *creds; - - int rc, cb_rc = NL_OK, done = 0; - - do { - unsigned char *buf; - int i, rem, flags; - struct nlmsghdr *nlh; - struct nlmsgerr *nlme; - struct nl_msg *msg; - - done = 0; - rc = nl_recv(sk, &nla, &buf, &creds); - if (rc < 0) - break; - - nlmsg_for_each_msg(nlh, (struct nlmsghdr *) buf, rc, rem) { - - if (rc <= 0 || cb_rc == NL_STOP) - break; - - /* Check for callbacks */ - - msg = (struct nl_msg *) malloc(sizeof(struct nl_msg)); - memset(msg, 0, sizeof(*msg)); - msg->nm_nlh = nlh; - - /* Check netlink message type */ - - switch (msg->nm_nlh->nlmsg_type) { - case NLMSG_ERROR: /* Used for ACK too */ - /* Certainly we should be doing some - * checking here to make sure this - * message is intended for us */ - nlme = nlmsg_data(msg->nm_nlh); - if (nlme->error == 0) - msg->nm_nlh->nlmsg_flags |= NLM_F_ACK; - - rc = nlme->error; - cb_rc = cb->cb_err(&nla, nlme, cb->cb_err_arg); - nlme = NULL; - break; - - case NLMSG_DONE: - done = 1; - - case NLMSG_OVERRUN: - case NLMSG_NOOP: - default: - break; - }; - - for (i = 0; i <= NL_CB_TYPE_MAX; i++) { - - if (cb->cb_set[i]) { - switch (i) { - case NL_CB_VALID: - if (rc > 0) - cb_rc = cb->cb_set[i](msg, cb->cb_args[i]); - break; - - case NL_CB_FINISH: - if ((msg->nm_nlh->nlmsg_flags & NLM_F_MULTI) && - (msg->nm_nlh->nlmsg_type & NLMSG_DONE)) - cb_rc = cb->cb_set[i](msg, cb->cb_args[i]); - - break; - - case NL_CB_ACK: - if (msg->nm_nlh->nlmsg_flags & NLM_F_ACK) - cb_rc = cb->cb_set[i](msg, cb->cb_args[i]); - - break; - default: - break; - } - } - } - - free(msg); - if (done) - break; - } - free(buf); - buf = NULL; - - if (done) - break; - } while (rc > 0 && cb_rc != NL_STOP); - -success: -fail: - return rc; -} - -/* Send raw data over netlink socket */ -int nl_send(struct nl_sock *sk, struct nl_msg *msg) -{ - struct nlmsghdr *nlh = nlmsg_hdr(msg); - struct iovec msg_iov; - - /* Create IO vector with Netlink message */ - msg_iov.iov_base = nlh; - msg_iov.iov_len = nlh->nlmsg_len; - - return nl_send_iovec(sk, msg, &msg_iov, 1); -} - -/* Send netlink message */ -int nl_send_iovec(struct nl_sock *sk, struct nl_msg *msg, - struct iovec *iov, unsigned iovlen) -{ - int rc; - - /* Socket message */ - struct msghdr mh = { - .msg_name = (void *) &sk->s_peer, - .msg_namelen = sizeof(sk->s_peer), - .msg_iov = iov, - .msg_iovlen = iovlen, - .msg_control = NULL, - .msg_controllen = 0, - .msg_flags = 0 - }; - - /* Send message and verify sent */ - rc = nl_sendmsg(sk, (struct nl_msg *) &mh, 0); - if (rc < 0) - fprintf(stderr, "Error sending netlink message: %d\n", errno); - return rc; - -} - -/* Send netlink message with control over sendmsg() message header */ -int nl_sendmsg(struct nl_sock *sk, struct nl_msg *msg, struct msghdr *hdr) -{ - return sendmsg(sk->s_fd, (struct msghdr *) msg, (int) hdr); -} - -/* Create and connect netlink socket */ -int nl_connect(struct nl_sock *sk, int protocol) -{ - struct sockaddr addr; - socklen_t addrlen; - int rc; - - /* Create RX socket */ - sk->s_fd = socket(PF_NETLINK, SOCK_RAW, protocol); - if (sk->s_fd < 0) - return -errno; - - /* Set size of RX and TX buffers */ - if (nl_socket_set_buffer_size(sk, NL_BUFFER_SZ, NL_BUFFER_SZ) < 0) - return -errno; - - /* Bind RX socket */ - rc = bind(sk->s_fd, (struct sockaddr *)&sk->s_local, \ - sizeof(sk->s_local)); - if (rc < 0) - return -errno; - addrlen = sizeof(addr); - getsockname(sk->s_fd, &addr, &addrlen); - - return 0; - -} diff --git a/libnl_2/socket.c b/libnl_2/socket.c deleted file mode 100644 index e94eb9e..0000000 --- a/libnl_2/socket.c +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -/* NOTICE: This is a clean room re-implementation of libnl */ - -#include <errno.h> -#include <unistd.h> -#include <malloc.h> -#include <sys/time.h> -#include <sys/socket.h> -#include "netlink-types.h" - -/* Join group */ -int nl_socket_add_membership(struct nl_sock *sk, int group) -{ - return setsockopt(sk->s_fd, SOL_NETLINK, - NETLINK_ADD_MEMBERSHIP, &group, sizeof(group)); -} - -/* Allocate new netlink socket. */ -static struct nl_sock *_nl_socket_alloc(void) -{ - struct nl_sock *sk; - struct timeval tv; - struct nl_cb *cb; - - sk = (struct nl_sock *) malloc(sizeof(struct nl_sock)); - if (!sk) - return NULL; - memset(sk, 0, sizeof(*sk)); - - /* Get current time */ - - if (gettimeofday(&tv, NULL)) - goto fail; - else - sk->s_seq_next = (int) tv.tv_sec; - - /* Create local socket */ - sk->s_local.nl_family = AF_NETLINK; - sk->s_local.nl_pid = 0; /* Kernel fills in pid */ - sk->s_local.nl_groups = 0; /* No groups */ - - /* Create peer socket */ - sk->s_peer.nl_family = AF_NETLINK; - sk->s_peer.nl_pid = 0; /* Kernel */ - sk->s_peer.nl_groups = 0; /* No groups */ - - return sk; -fail: - free(sk); - return NULL; -} - -/* Allocate new netlink socket. */ -struct nl_sock *nl_socket_alloc(void) -{ - struct nl_sock *sk = _nl_socket_alloc(); - struct nl_cb *cb; - - if (!sk) - return NULL; - - cb = nl_cb_alloc(NL_CB_DEFAULT); - if (!cb) - goto cb_fail; - sk->s_cb = cb; - return sk; -cb_fail: - free(sk); - return NULL; -} - -/* Allocate new socket with custom callbacks. */ -struct nl_sock *nl_socket_alloc_cb(struct nl_cb *cb) -{ - struct nl_sock *sk = _nl_socket_alloc(); - - if (!sk) - return NULL; - - sk->s_cb = cb; - nl_cb_get(cb); - - return sk; -} - -/* Free a netlink socket. */ -void nl_socket_free(struct nl_sock *sk) -{ - nl_cb_put(sk->s_cb); - close(sk->s_fd); - free(sk); -} - -/* Sets socket buffer size of netlink socket */ -int nl_socket_set_buffer_size(struct nl_sock *sk, int rxbuf, int txbuf) -{ - if (setsockopt(sk->s_fd, SOL_SOCKET, SO_SNDBUF, \ - &rxbuf, (socklen_t) sizeof(rxbuf))) - goto error; - - if (setsockopt(sk->s_fd, SOL_SOCKET, SO_RCVBUF, \ - &txbuf, (socklen_t) sizeof(txbuf))) - goto error; - - return 0; -error: - return -errno; - -} - -int nl_socket_get_fd(struct nl_sock *sk) -{ - return sk->s_fd; -} - -void nl_socket_set_cb(struct nl_sock *sk, struct nl_cb *cb) -{ - nl_cb_put(sk->s_cb); - sk->s_cb = cb; - nl_cb_get(cb); -} - -struct nl_cb *nl_socket_get_cb(struct nl_sock *sk) -{ - return nl_cb_get(sk->s_cb); -} diff --git a/libpixelflinger/Android.mk b/libpixelflinger/Android.mk index fe50cc6..484cf50 100644 --- a/libpixelflinger/Android.mk +++ b/libpixelflinger/Android.mk @@ -7,13 +7,13 @@ include $(CLEAR_VARS) include $(CLEAR_VARS) PIXELFLINGER_SRC_FILES:= \ - codeflinger/ARMAssemblerInterface.cpp \ - codeflinger/ARMAssemblerProxy.cpp \ - codeflinger/CodeCache.cpp \ - codeflinger/GGLAssembler.cpp \ - codeflinger/load_store.cpp \ - codeflinger/blending.cpp \ - codeflinger/texturing.cpp \ + codeflinger/ARMAssemblerInterface.cpp \ + codeflinger/ARMAssemblerProxy.cpp \ + codeflinger/CodeCache.cpp \ + codeflinger/GGLAssembler.cpp \ + codeflinger/load_store.cpp \ + codeflinger/blending.cpp \ + codeflinger/texturing.cpp \ codeflinger/tinyutils/SharedBuffer.cpp \ codeflinger/tinyutils/VectorImpl.cpp \ fixed.cpp.arm \ @@ -26,39 +26,28 @@ PIXELFLINGER_SRC_FILES:= \ raster.cpp \ buffer.cpp -ifeq ($(TARGET_ARCH),arm) -ifeq ($(TARGET_ARCH_VERSION),armv7-a) -PIXELFLINGER_SRC_FILES += col32cb16blend_neon.S -PIXELFLINGER_SRC_FILES += col32cb16blend.S -else -PIXELFLINGER_SRC_FILES += t32cb16blend.S -PIXELFLINGER_SRC_FILES += col32cb16blend.S -endif -endif +PIXELFLINGER_CFLAGS := -fstrict-aliasing -fomit-frame-pointer -ifeq ($(TARGET_ARCH),arm) -PIXELFLINGER_SRC_FILES += codeflinger/ARMAssembler.cpp -PIXELFLINGER_SRC_FILES += codeflinger/disassem.c -# special optimization flags for pixelflinger -PIXELFLINGER_CFLAGS += -fstrict-aliasing -fomit-frame-pointer -endif +PIXELFLINGER_SRC_FILES_arm := \ + codeflinger/ARMAssembler.cpp \ + codeflinger/disassem.c \ + col32cb16blend.S \ + t32cb16blend.S \ -ifeq ($(TARGET_ARCH),mips) -PIXELFLINGER_SRC_FILES += codeflinger/MIPSAssembler.cpp -PIXELFLINGER_SRC_FILES += codeflinger/mips_disassem.c -PIXELFLINGER_SRC_FILES += arch-mips/t32cb16blend.S -PIXELFLINGER_CFLAGS += -fstrict-aliasing -fomit-frame-pointer +ifeq ($(ARCH_ARM_HAVE_NEON),true) +PIXELFLINGER_SRC_FILES_arm += col32cb16blend_neon.S endif -LOCAL_SHARED_LIBRARIES := libcutils liblog +PIXELFLINGER_SRC_FILES_arm64 := \ + codeflinger/Arm64Assembler.cpp \ + codeflinger/Arm64Disassembler.cpp \ + arch-arm64/col32cb16blend.S \ + arch-arm64/t32cb16blend.S \ -ifeq ($(TARGET_ARCH),arm64) -PIXELFLINGER_SRC_FILES += arch-arm64/t32cb16blend.S -PIXELFLINGER_SRC_FILES += arch-arm64/col32cb16blend.S -PIXELFLINGER_SRC_FILES += codeflinger/Arm64Assembler.cpp -PIXELFLINGER_SRC_FILES += codeflinger/Arm64Disassembler.cpp -PIXELFLINGER_CFLAGS += -fstrict-aliasing -fomit-frame-pointer -endif +PIXELFLINGER_SRC_FILES_mips := \ + codeflinger/MIPSAssembler.cpp \ + codeflinger/mips_disassem.c \ + arch-mips/t32cb16blend.S \ # # Shared library @@ -66,7 +55,11 @@ endif LOCAL_MODULE:= libpixelflinger LOCAL_SRC_FILES := $(PIXELFLINGER_SRC_FILES) +LOCAL_SRC_FILES_arm := $(PIXELFLINGER_SRC_FILES_arm) +LOCAL_SRC_FILES_arm64 := $(PIXELFLINGER_SRC_FILES_arm64) +LOCAL_SRC_FILES_mips := $(PIXELFLINGER_SRC_FILES_mips) LOCAL_CFLAGS := $(PIXELFLINGER_CFLAGS) +LOCAL_SHARED_LIBRARIES := libcutils liblog ifneq ($(BUILD_TINY_ANDROID),true) # Really this should go away entirely or at least not depend on @@ -83,6 +76,9 @@ include $(BUILD_SHARED_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE:= libpixelflinger_static LOCAL_SRC_FILES := $(PIXELFLINGER_SRC_FILES) +LOCAL_SRC_FILES_arm := $(PIXELFLINGER_SRC_FILES_arm) +LOCAL_SRC_FILES_arm64 := $(PIXELFLINGER_SRC_FILES_arm64) +LOCAL_SRC_FILES_mips := $(PIXELFLINGER_SRC_FILES_mips) LOCAL_CFLAGS := $(PIXELFLINGER_CFLAGS) include $(BUILD_STATIC_LIBRARY) diff --git a/libpixelflinger/buffer.cpp b/libpixelflinger/buffer.cpp index cbdab5a..dcb95c5 100644 --- a/libpixelflinger/buffer.cpp +++ b/libpixelflinger/buffer.cpp @@ -130,7 +130,7 @@ void read_pixel(const surface_t* s, context_t* c, } } -void readRGB565(const surface_t* s, context_t* c, +void readRGB565(const surface_t* s, context_t* /*c*/, uint32_t x, uint32_t y, pixel_t* pixel) { uint16_t v = *(reinterpret_cast<uint16_t*>(s->data) + (x + (s->stride * y))); @@ -144,7 +144,7 @@ void readRGB565(const surface_t* s, context_t* c, pixel->s[3] = 5; } -void readABGR8888(const surface_t* s, context_t* c, +void readABGR8888(const surface_t* s, context_t* /*c*/, uint32_t x, uint32_t y, pixel_t* pixel) { uint32_t v = *(reinterpret_cast<uint32_t*>(s->data) + (x + (s->stride * y))); diff --git a/libpixelflinger/codeflinger/Arm64Assembler.cpp b/libpixelflinger/codeflinger/Arm64Assembler.cpp index f37072a..bd11818 100644 --- a/libpixelflinger/codeflinger/Arm64Assembler.cpp +++ b/libpixelflinger/codeflinger/Arm64Assembler.cpp @@ -273,7 +273,7 @@ void ArmToArm64Assembler::B(int cc, const char* label) *mPC++ = (0x54 << 24) | cc; } -void ArmToArm64Assembler::BL(int cc, const char* label) +void ArmToArm64Assembler::BL(int /*cc*/, const char* /*label*/) { NOT_IMPLEMENTED(); //Not Required } @@ -289,7 +289,7 @@ void ArmToArm64Assembler::prolog() *mPC++ = A64_MOVZ_X(mZeroReg,0,0); } -void ArmToArm64Assembler::epilog(uint32_t touched) +void ArmToArm64Assembler::epilog(uint32_t /*touched*/) { // write epilog code static const int XLR = 30; @@ -530,23 +530,23 @@ void ArmToArm64Assembler::MUL(int cc, int s, int Rd, int Rm, int Rs) if(s != 0) { NOT_IMPLEMENTED(); return;} //Not required *mPC++ = A64_MADD_W(Rd, Rm, Rs, mZeroReg); } -void ArmToArm64Assembler::UMULL(int cc, int s, - int RdLo, int RdHi, int Rm, int Rs) +void ArmToArm64Assembler::UMULL(int /*cc*/, int /*s*/, + int /*RdLo*/, int /*RdHi*/, int /*Rm*/, int /*Rs*/) { NOT_IMPLEMENTED(); //Not required } -void ArmToArm64Assembler::UMUAL(int cc, int s, - int RdLo, int RdHi, int Rm, int Rs) +void ArmToArm64Assembler::UMUAL(int /*cc*/, int /*s*/, + int /*RdLo*/, int /*RdHi*/, int /*Rm*/, int /*Rs*/) { NOT_IMPLEMENTED(); //Not required } -void ArmToArm64Assembler::SMULL(int cc, int s, - int RdLo, int RdHi, int Rm, int Rs) +void ArmToArm64Assembler::SMULL(int /*cc*/, int /*s*/, + int /*RdLo*/, int /*RdHi*/, int /*Rm*/, int /*Rs*/) { NOT_IMPLEMENTED(); //Not required } -void ArmToArm64Assembler::SMUAL(int cc, int s, - int RdLo, int RdHi, int Rm, int Rs) +void ArmToArm64Assembler::SMUAL(int /*cc*/, int /*s*/, + int /*RdLo*/, int /*RdHi*/, int /*Rm*/, int /*Rs*/) { NOT_IMPLEMENTED(); //Not required } @@ -554,15 +554,15 @@ void ArmToArm64Assembler::SMUAL(int cc, int s, // ---------------------------------------------------------------------------- // branches relative to PC... // ---------------------------------------------------------------------------- -void ArmToArm64Assembler::B(int cc, uint32_t* pc){ +void ArmToArm64Assembler::B(int /*cc*/, uint32_t* /*pc*/){ NOT_IMPLEMENTED(); //Not required } -void ArmToArm64Assembler::BL(int cc, uint32_t* pc){ +void ArmToArm64Assembler::BL(int /*cc*/, uint32_t* /*pc*/){ NOT_IMPLEMENTED(); //Not required } -void ArmToArm64Assembler::BX(int cc, int Rn){ +void ArmToArm64Assembler::BX(int /*cc*/, int /*Rn*/){ NOT_IMPLEMENTED(); //Not required } @@ -661,11 +661,11 @@ void ArmToArm64Assembler::LDRH(int cc, int Rd, int Rn, uint32_t op_type) { return dataTransfer(opLDRH, cc, Rd, Rn, op_type); } -void ArmToArm64Assembler::LDRSB(int cc, int Rd, int Rn, uint32_t offset) +void ArmToArm64Assembler::LDRSB(int /*cc*/, int /*Rd*/, int /*Rn*/, uint32_t /*offset*/) { NOT_IMPLEMENTED(); //Not required } -void ArmToArm64Assembler::LDRSH(int cc, int Rd, int Rn, uint32_t offset) +void ArmToArm64Assembler::LDRSH(int /*cc*/, int /*Rd*/, int /*Rn*/, uint32_t /*offset*/) { NOT_IMPLEMENTED(); //Not required } @@ -723,15 +723,15 @@ void ArmToArm64Assembler::STM(int cc, int dir, // ---------------------------------------------------------------------------- // special... // ---------------------------------------------------------------------------- -void ArmToArm64Assembler::SWP(int cc, int Rn, int Rd, int Rm) +void ArmToArm64Assembler::SWP(int /*cc*/, int /*Rn*/, int /*Rd*/, int /*Rm*/) { NOT_IMPLEMENTED(); //Not required } -void ArmToArm64Assembler::SWPB(int cc, int Rn, int Rd, int Rm) +void ArmToArm64Assembler::SWPB(int /*cc*/, int /*Rn*/, int /*Rd*/, int /*Rm*/) { NOT_IMPLEMENTED(); //Not required } -void ArmToArm64Assembler::SWI(int cc, uint32_t comment) +void ArmToArm64Assembler::SWI(int /*cc*/, uint32_t /*comment*/) { NOT_IMPLEMENTED(); //Not required } @@ -739,31 +739,31 @@ void ArmToArm64Assembler::SWI(int cc, uint32_t comment) // ---------------------------------------------------------------------------- // DSP instructions... // ---------------------------------------------------------------------------- -void ArmToArm64Assembler::PLD(int Rn, uint32_t offset) { +void ArmToArm64Assembler::PLD(int /*Rn*/, uint32_t /*offset*/) { NOT_IMPLEMENTED(); //Not required } -void ArmToArm64Assembler::CLZ(int cc, int Rd, int Rm) +void ArmToArm64Assembler::CLZ(int /*cc*/, int /*Rd*/, int /*Rm*/) { NOT_IMPLEMENTED(); //Not required } -void ArmToArm64Assembler::QADD(int cc, int Rd, int Rm, int Rn) +void ArmToArm64Assembler::QADD(int /*cc*/, int /*Rd*/, int /*Rm*/, int /*Rn*/) { NOT_IMPLEMENTED(); //Not required } -void ArmToArm64Assembler::QDADD(int cc, int Rd, int Rm, int Rn) +void ArmToArm64Assembler::QDADD(int /*cc*/, int /*Rd*/, int /*Rm*/, int /*Rn*/) { NOT_IMPLEMENTED(); //Not required } -void ArmToArm64Assembler::QSUB(int cc, int Rd, int Rm, int Rn) +void ArmToArm64Assembler::QSUB(int /*cc*/, int /*Rd*/, int /*Rm*/, int /*Rn*/) { NOT_IMPLEMENTED(); //Not required } -void ArmToArm64Assembler::QDSUB(int cc, int Rd, int Rm, int Rn) +void ArmToArm64Assembler::QDSUB(int /*cc*/, int /*Rd*/, int /*Rm*/, int /*Rn*/) { NOT_IMPLEMENTED(); //Not required } @@ -817,15 +817,15 @@ void ArmToArm64Assembler::SMLA(int cc, int xy, int Rd, int Rm, int Rs, int Rn) *mPC++ = A64_MADD_W(Rd, mTmpReg1, mTmpReg2, Rn); } -void ArmToArm64Assembler::SMLAL(int cc, int xy, - int RdHi, int RdLo, int Rs, int Rm) +void ArmToArm64Assembler::SMLAL(int /*cc*/, int /*xy*/, + int /*RdHi*/, int /*RdLo*/, int /*Rs*/, int /*Rm*/) { NOT_IMPLEMENTED(); //Not required return; } -void ArmToArm64Assembler::SMLAW(int cc, int y, - int Rd, int Rm, int Rs, int Rn) +void ArmToArm64Assembler::SMLAW(int /*cc*/, int /*y*/, + int /*Rd*/, int /*Rm*/, int /*Rs*/, int /*Rn*/) { NOT_IMPLEMENTED(); //Not required return; @@ -890,13 +890,13 @@ uint32_t ArmToArm64Assembler::reg_imm(int Rm, int type, uint32_t shift) return OPERAND_REG_IMM; } -uint32_t ArmToArm64Assembler::reg_rrx(int Rm) +uint32_t ArmToArm64Assembler::reg_rrx(int /*Rm*/) { NOT_IMPLEMENTED(); return OPERAND_UNSUPPORTED; } -uint32_t ArmToArm64Assembler::reg_reg(int Rm, int type, int Rs) +uint32_t ArmToArm64Assembler::reg_reg(int /*Rm*/, int /*type*/, int /*Rs*/) { NOT_IMPLEMENTED(); //Not required return OPERAND_UNSUPPORTED; @@ -937,7 +937,7 @@ uint32_t ArmToArm64Assembler::reg_scale_pre(int Rm, int type, } } -uint32_t ArmToArm64Assembler::reg_scale_post(int Rm, int type, uint32_t shift) +uint32_t ArmToArm64Assembler::reg_scale_post(int /*Rm*/, int /*type*/, uint32_t /*shift*/) { NOT_IMPLEMENTED(); //Not required return OPERAND_UNSUPPORTED; @@ -975,7 +975,7 @@ uint32_t ArmToArm64Assembler::reg_pre(int Rm, int W) } } -uint32_t ArmToArm64Assembler::reg_post(int Rm) +uint32_t ArmToArm64Assembler::reg_post(int /*Rm*/) { NOT_IMPLEMENTED(); //Not required return OPERAND_UNSUPPORTED; diff --git a/libpixelflinger/codeflinger/CodeCache.cpp b/libpixelflinger/codeflinger/CodeCache.cpp index 4fe30d9..cfd2b37 100644 --- a/libpixelflinger/codeflinger/CodeCache.cpp +++ b/libpixelflinger/codeflinger/CodeCache.cpp @@ -89,7 +89,7 @@ static mspace getMspace() gExecutableStore = mmap(NULL, kMaxCodeCacheCapacity, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE, fd, 0); - LOG_ALWAYS_FATAL_IF(gExecutableStore == NULL, + LOG_ALWAYS_FATAL_IF(gExecutableStore == MAP_FAILED, "Creating code cache, mmap failed with error " "'%s'", strerror(errno)); close(fd); @@ -201,13 +201,9 @@ int CodeCache::cache( const AssemblyKeyBase& keyBase, mCacheInUse += assemblySize; mWhen++; // synchronize caches... -#if defined(__arm__) || defined(__mips__) || defined(__aarch64__) - const long base = long(assembly->base()); - const long curr = base + long(assembly->size()); - err = cacheflush(base, curr, 0); - ALOGE_IF(err, "cacheflush error %s\n", - strerror(errno)); -#endif + void* base = assembly->base(); + void* curr = (uint8_t*)base + assembly->size(); + __builtin___clear_cache(base, curr); } pthread_mutex_unlock(&mLock); diff --git a/libpixelflinger/codeflinger/GGLAssembler.cpp b/libpixelflinger/codeflinger/GGLAssembler.cpp index 2422d7b..325caba 100644 --- a/libpixelflinger/codeflinger/GGLAssembler.cpp +++ b/libpixelflinger/codeflinger/GGLAssembler.cpp @@ -694,7 +694,7 @@ void GGLAssembler::build_coverage_application(component_t& fragment, // --------------------------------------------------------------------------- void GGLAssembler::build_alpha_test(component_t& fragment, - const fragment_parts_t& parts) + const fragment_parts_t& /*parts*/) { if (mAlphaTest != GGL_ALWAYS) { comment("Alpha Test"); @@ -796,7 +796,7 @@ void GGLAssembler::build_iterate_z(const fragment_parts_t& parts) } } -void GGLAssembler::build_iterate_f(const fragment_parts_t& parts) +void GGLAssembler::build_iterate_f(const fragment_parts_t& /*parts*/) { const needs_t& needs = mBuilderContext.needs; if (GGL_READ_NEEDS(P_FOG, needs.p)) { diff --git a/libpixelflinger/codeflinger/disassem.c b/libpixelflinger/codeflinger/disassem.c index aeb8034..39dd614 100644 --- a/libpixelflinger/codeflinger/disassem.c +++ b/libpixelflinger/codeflinger/disassem.c @@ -301,19 +301,14 @@ static u_int disassemble_readword(u_int address); static void disassemble_printaddr(u_int address); u_int -disasm(const disasm_interface_t *di, u_int loc, int altfmt) +disasm(const disasm_interface_t *di, u_int loc, int __unused altfmt) { const struct arm32_insn *i_ptr = &arm32_i[0]; - - u_int insn; - int matchp; + u_int insn = di->di_readword(loc); + int matchp = 0; int branch; char* f_ptr; - int fmt; - - fmt = 0; - matchp = 0; - insn = di->di_readword(loc); + int fmt = 0; /* di->di_printf("loc=%08x insn=%08x : ", loc, insn);*/ @@ -670,7 +665,7 @@ disasm_insn_ldrhstrh(const disasm_interface_t *di, u_int insn, u_int loc) } static void -disasm_insn_ldcstc(const disasm_interface_t *di, u_int insn, u_int loc) +disasm_insn_ldcstc(const disasm_interface_t *di, u_int insn, u_int __unused loc) { if (((insn >> 8) & 0xf) == 1) di->di_printf("f%d, ", (insn >> 12) & 0x07); diff --git a/libpixelflinger/codeflinger/disassem.h b/libpixelflinger/codeflinger/disassem.h index 02747cd..c7c60b6 100644 --- a/libpixelflinger/codeflinger/disassem.h +++ b/libpixelflinger/codeflinger/disassem.h @@ -49,8 +49,8 @@ extern "C" { typedef struct { u_int (*di_readword)(u_int); - void (*di_printaddr)(u_int); - void (*di_printf)(const char *, ...); + void (*di_printaddr)(u_int); + int (*di_printf)(const char *, ...); } disasm_interface_t; /* Prototypes for callable functions */ diff --git a/libpixelflinger/codeflinger/texturing.cpp b/libpixelflinger/codeflinger/texturing.cpp index b2cfbb3..81950bf 100644 --- a/libpixelflinger/codeflinger/texturing.cpp +++ b/libpixelflinger/codeflinger/texturing.cpp @@ -694,7 +694,7 @@ void GGLAssembler::build_iterate_texture_coordinates( } void GGLAssembler::filter8( - const fragment_parts_t& parts, + const fragment_parts_t& /*parts*/, pixel_t& texel, const texture_unit_t& tmu, int U, int V, pointer_t& txPtr, int FRAC_BITS) @@ -761,7 +761,7 @@ void GGLAssembler::filter8( } void GGLAssembler::filter16( - const fragment_parts_t& parts, + const fragment_parts_t& /*parts*/, pixel_t& texel, const texture_unit_t& tmu, int U, int V, pointer_t& txPtr, int FRAC_BITS) @@ -879,10 +879,10 @@ void GGLAssembler::filter16( } void GGLAssembler::filter24( - const fragment_parts_t& parts, - pixel_t& texel, const texture_unit_t& tmu, - int U, int V, pointer_t& txPtr, - int FRAC_BITS) + const fragment_parts_t& /*parts*/, + pixel_t& texel, const texture_unit_t& /*tmu*/, + int /*U*/, int /*V*/, pointer_t& txPtr, + int /*FRAC_BITS*/) { // not supported yet (currently disabled) load(txPtr, texel, 0); @@ -989,8 +989,8 @@ void GGLAssembler::filter32( } #else void GGLAssembler::filter32( - const fragment_parts_t& parts, - pixel_t& texel, const texture_unit_t& tmu, + const fragment_parts_t& /*parts*/, + pixel_t& texel, const texture_unit_t& /*tmu*/, int U, int V, pointer_t& txPtr, int FRAC_BITS) { diff --git a/libpixelflinger/picker.cpp b/libpixelflinger/picker.cpp index 030ef19..aa55229 100644 --- a/libpixelflinger/picker.cpp +++ b/libpixelflinger/picker.cpp @@ -26,7 +26,7 @@ namespace android { // ---------------------------------------------------------------------------- -void ggl_init_picker(context_t* c) +void ggl_init_picker(context_t* /*c*/) { } diff --git a/libpixelflinger/pixelflinger.cpp b/libpixelflinger/pixelflinger.cpp index 84e584e..ea5bc8e 100644 --- a/libpixelflinger/pixelflinger.cpp +++ b/libpixelflinger/pixelflinger.cpp @@ -662,7 +662,7 @@ void ggl_enable_dither(context_t* c, int enable) } } -void ggl_enable_stencil_test(context_t* c, int enable) +void ggl_enable_stencil_test(context_t* /*c*/, int /*enable*/) { } diff --git a/libpixelflinger/raster.cpp b/libpixelflinger/raster.cpp index 32b2a97..26d8e45 100644 --- a/libpixelflinger/raster.cpp +++ b/libpixelflinger/raster.cpp @@ -51,7 +51,7 @@ void ggl_rasterPos2i(void* con, GGLint x, GGLint y) } void ggl_copyPixels(void* con, GGLint xs, GGLint ys, - GGLsizei width, GGLsizei height, GGLenum type) + GGLsizei width, GGLsizei height, GGLenum /*type*/) { GGL_CONTEXT(c, con); diff --git a/libpixelflinger/scanline.cpp b/libpixelflinger/scanline.cpp index aa23ca6..26b9a3e 100644 --- a/libpixelflinger/scanline.cpp +++ b/libpixelflinger/scanline.cpp @@ -26,6 +26,10 @@ #include <cutils/memory.h> #include <cutils/log.h> +#ifdef __arm__ +#include <machine/cpu-features.h> +#endif + #include "buffer.h" #include "scanline.h" @@ -35,7 +39,7 @@ #include "codeflinger/ARMAssembler.h" #elif defined(__aarch64__) #include "codeflinger/Arm64Assembler.h" -#elif defined(__mips__) +#elif defined(__mips__) && !defined(__LP64__) #include "codeflinger/MIPSAssembler.h" #endif //#include "codeflinger/ARMAssemblerOptimizer.h" @@ -55,7 +59,7 @@ # define ANDROID_CODEGEN ANDROID_CODEGEN_GENERATED #endif -#if defined(__arm__) || defined(__mips__) || defined(__aarch64__) +#if defined(__arm__) || (defined(__mips__) && !defined(__LP64__)) || defined(__aarch64__) # define ANDROID_ARM_CODEGEN 1 #else # define ANDROID_ARM_CODEGEN 0 @@ -69,7 +73,7 @@ */ #define DEBUG_NEEDS 0 -#ifdef __mips__ +#if defined( __mips__) && !defined(__LP64__) #define ASSEMBLY_SCRATCH_SIZE 4096 #elif defined(__aarch64__) #define ASSEMBLY_SCRATCH_SIZE 8192 @@ -130,7 +134,7 @@ extern "C" void scanline_col32cb16blend_arm(uint16_t *dst, uint32_t col, size_t #elif defined(__aarch64__) extern "C" void scanline_t32cb16blend_arm64(uint16_t*, uint32_t*, size_t); extern "C" void scanline_col32cb16blend_arm64(uint16_t *dst, uint32_t col, size_t ct); -#elif defined(__mips__) +#elif defined(__mips__) && !defined(__LP64__) extern "C" void scanline_t32cb16blend_mips(uint16_t*, uint32_t*, size_t); #endif @@ -408,10 +412,10 @@ static void pick_scanline(context_t* c) GGLAssembler assembler( new ArmToArm64Assembler(a) ); #endif // generate the scanline code for the given needs - int err = assembler.scanline(c->state.needs, c); + bool err = assembler.scanline(c->state.needs, c) != 0; if (ggl_likely(!err)) { // finally, cache this assembly - err = gCodeCache.cache(a->key(), a); + err = gCodeCache.cache(a->key(), a) < 0; } if (ggl_unlikely(err)) { ALOGE("error generating or caching assembly. Reverting to NOP."); @@ -534,7 +538,7 @@ static inline int blendfactor(uint32_t x, uint32_t size, uint32_t def = 0) return x; } -void blend_factor(context_t* c, pixel_t* r, +void blend_factor(context_t* /*c*/, pixel_t* r, uint32_t factor, const pixel_t* src, const pixel_t* dst) { switch (factor) { @@ -1161,7 +1165,7 @@ protected: * blender.blend(<32-bit-src-pixel-value>,<ptr-to-16-bit-dest-pixel>) */ struct blender_32to16 { - blender_32to16(context_t* c) { } + blender_32to16(context_t* /*c*/) { } void write(uint32_t s, uint16_t* dst) { if (s == 0) return; @@ -1218,7 +1222,7 @@ struct blender_32to16 { * where dstFactor=srcA*(1-srcA) srcFactor=srcA */ struct blender_32to16_srcA { - blender_32to16_srcA(const context_t* c) { } + blender_32to16_srcA(const context_t* /*c*/) { } void write(uint32_t s, uint16_t* dst) { if (!s) { return; @@ -2171,7 +2175,7 @@ last_one: void scanline_t32cb16blend(context_t* c) { -#if ((ANDROID_CODEGEN >= ANDROID_CODEGEN_ASM) && (defined(__arm__) || defined(__mips__) || defined(__aarch64__))) +#if ((ANDROID_CODEGEN >= ANDROID_CODEGEN_ASM) && (defined(__arm__) || (defined(__mips__) && !defined(__LP64__)) || defined(__aarch64__))) int32_t x = c->iterators.xl; size_t ct = c->iterators.xr - x; int32_t y = c->iterators.y; @@ -2317,7 +2321,7 @@ void scanline_set(context_t* c) memset(dst, 0xFF, size); } -void scanline_noop(context_t* c) +void scanline_noop(context_t* /*c*/) { } diff --git a/libpixelflinger/tests/arch-arm64/assembler/Android.mk b/libpixelflinger/tests/arch-arm64/assembler/Android.mk index 36db49c..eca36ef 100644 --- a/libpixelflinger/tests/arch-arm64/assembler/Android.mk +++ b/libpixelflinger/tests/arch-arm64/assembler/Android.mk @@ -16,4 +16,6 @@ LOCAL_MODULE:= test-pixelflinger-arm64-assembler-test LOCAL_MODULE_TAGS := tests -include $(BUILD_EXECUTABLE) +LOCAL_MULTILIB := 64 + +include $(BUILD_NATIVE_TEST) diff --git a/libpixelflinger/tests/arch-arm64/assembler/arm64_assembler_test.cpp b/libpixelflinger/tests/arch-arm64/assembler/arm64_assembler_test.cpp index 84381d5..456be58 100644 --- a/libpixelflinger/tests/arch-arm64/assembler/arm64_assembler_test.cpp +++ b/libpixelflinger/tests/arch-arm64/assembler/arm64_assembler_test.cpp @@ -405,11 +405,11 @@ dataTransferTest_t dataTransferTests [] = }; -int flushcache() +void flushcache() { const long base = long(instrMem); const long curr = base + long(instrMemSize); - return cacheflush(base, curr, 0); + __builtin___clear_cache((void*)base, (void*)curr); } void dataOpTest(dataOpTest_t test, ARMAssemblerInterface *a64asm, uint32_t Rd = 0, uint32_t Rn = 1, uint32_t Rm = 2, uint32_t Rs = 3) diff --git a/libpixelflinger/tests/arch-arm64/col32cb16blend/Android.mk b/libpixelflinger/tests/arch-arm64/col32cb16blend/Android.mk index ac890c7..3368eb0 100644 --- a/libpixelflinger/tests/arch-arm64/col32cb16blend/Android.mk +++ b/libpixelflinger/tests/arch-arm64/col32cb16blend/Android.mk @@ -13,4 +13,6 @@ LOCAL_MODULE:= test-pixelflinger-arm64-col32cb16blend LOCAL_MODULE_TAGS := tests -include $(BUILD_EXECUTABLE) +LOCAL_MULTILIB := 64 + +include $(BUILD_NATIVE_TEST) diff --git a/libpixelflinger/tests/arch-arm64/disassembler/Android.mk b/libpixelflinger/tests/arch-arm64/disassembler/Android.mk index baf4070..8f62f09 100644 --- a/libpixelflinger/tests/arch-arm64/disassembler/Android.mk +++ b/libpixelflinger/tests/arch-arm64/disassembler/Android.mk @@ -14,4 +14,6 @@ LOCAL_MODULE:= test-pixelflinger-arm64-disassembler-test LOCAL_MODULE_TAGS := tests -include $(BUILD_EXECUTABLE) +LOCAL_MULTILIB := 64 + +include $(BUILD_NATIVE_TEST) diff --git a/libpixelflinger/tests/arch-arm64/t32cb16blend/Android.mk b/libpixelflinger/tests/arch-arm64/t32cb16blend/Android.mk index 1cce1bd..8e5ec5e 100644 --- a/libpixelflinger/tests/arch-arm64/t32cb16blend/Android.mk +++ b/libpixelflinger/tests/arch-arm64/t32cb16blend/Android.mk @@ -13,4 +13,6 @@ LOCAL_MODULE:= test-pixelflinger-arm64-t32cb16blend LOCAL_MODULE_TAGS := tests -include $(BUILD_EXECUTABLE) +LOCAL_MULTILIB := 64 + +include $(BUILD_NATIVE_TEST) diff --git a/libpixelflinger/tests/codegen/Android.mk b/libpixelflinger/tests/codegen/Android.mk index aa320fc..bc07015 100644 --- a/libpixelflinger/tests/codegen/Android.mk +++ b/libpixelflinger/tests/codegen/Android.mk @@ -15,4 +15,4 @@ LOCAL_MODULE:= test-opengl-codegen LOCAL_MODULE_TAGS := tests -include $(BUILD_EXECUTABLE) +include $(BUILD_NATIVE_TEST) diff --git a/libpixelflinger/tests/codegen/codegen.cpp b/libpixelflinger/tests/codegen/codegen.cpp index e9f6c61..46c1ccc 100644 --- a/libpixelflinger/tests/codegen/codegen.cpp +++ b/libpixelflinger/tests/codegen/codegen.cpp @@ -52,7 +52,7 @@ static void ggl_test_codegen(uint32_t n, uint32_t p, uint32_t t0, uint32_t t1) GGLAssembler assembler( new ARMAssembler(a) ); #endif -#if defined(__mips__) +#if defined(__mips__) && !defined(__LP64__) GGLAssembler assembler( new ArmToMipsAssembler(a) ); #endif diff --git a/libpixelflinger/tests/gglmul/Android.mk b/libpixelflinger/tests/gglmul/Android.mk index 64f88b7..f479fa1 100644 --- a/libpixelflinger/tests/gglmul/Android.mk +++ b/libpixelflinger/tests/gglmul/Android.mk @@ -13,4 +13,4 @@ LOCAL_MODULE:= test-pixelflinger-gglmul LOCAL_MODULE_TAGS := tests -include $(BUILD_EXECUTABLE) +include $(BUILD_NATIVE_TEST) diff --git a/libpixelflinger/tests/gglmul/gglmul_test.cpp b/libpixelflinger/tests/gglmul/gglmul_test.cpp index 073368e..5d460d6 100644 --- a/libpixelflinger/tests/gglmul/gglmul_test.cpp +++ b/libpixelflinger/tests/gglmul/gglmul_test.cpp @@ -28,6 +28,7 @@ #include <stdio.h> #include <stdint.h> +#include <inttypes.h> #include "private/pixelflinger/ggl_fixed.h" @@ -260,12 +261,12 @@ void gglMulii_test() if(actual == expected) printf(" Passed\n"); else - printf(" Failed Actual(%ld) Expected(%ld)\n", + printf(" Failed Actual(%" PRId64 ") Expected(%" PRId64 ")\n", actual, expected); } } -int main(int argc, char** argv) +int main(int /*argc*/, char** /*argv*/) { gglClampx_test(); gglClz_test(); diff --git a/libsparse/Android.mk b/libsparse/Android.mk index 9025cc0..02ab412 100644 --- a/libsparse/Android.mk +++ b/libsparse/Android.mk @@ -17,6 +17,7 @@ LOCAL_SRC_FILES := $(libsparse_src_files) LOCAL_MODULE := libsparse_host LOCAL_STATIC_LIBRARIES := libz LOCAL_C_INCLUDES += $(LOCAL_PATH)/include external/zlib +LOCAL_CFLAGS := -Werror include $(BUILD_HOST_STATIC_LIBRARY) @@ -27,6 +28,7 @@ LOCAL_MODULE := libsparse LOCAL_C_INCLUDES += $(LOCAL_PATH)/include external/zlib LOCAL_SHARED_LIBRARIES := \ libz +LOCAL_CFLAGS := -Werror include $(BUILD_SHARED_LIBRARY) @@ -36,6 +38,7 @@ LOCAL_SRC_FILES := $(libsparse_src_files) LOCAL_MODULE := libsparse_static LOCAL_C_INCLUDES += $(LOCAL_PATH)/include external/zlib LOCAL_STATIC_LIBRARIES := libz +LOCAL_CFLAGS := -Werror include $(BUILD_STATIC_LIBRARY) @@ -48,6 +51,7 @@ LOCAL_MODULE_STEM := simg2img LOCAL_STATIC_LIBRARIES := \ libsparse_host \ libz +LOCAL_CFLAGS := -Werror include $(BUILD_HOST_EXECUTABLE) @@ -58,6 +62,7 @@ LOCAL_MODULE := simg2img LOCAL_STATIC_LIBRARIES := \ libsparse_static \ libz +LOCAL_CFLAGS := -Werror include $(BUILD_EXECUTABLE) @@ -69,6 +74,7 @@ LOCAL_MODULE_STEM := img2simg LOCAL_STATIC_LIBRARIES := \ libsparse_host \ libz +LOCAL_CFLAGS := -Werror include $(BUILD_HOST_EXECUTABLE) @@ -78,6 +84,7 @@ LOCAL_MODULE := img2simg LOCAL_STATIC_LIBRARIES := \ libsparse_static \ libz +LOCAL_CFLAGS := -Werror include $(BUILD_EXECUTABLE) @@ -87,6 +94,7 @@ LOCAL_MODULE := simg2simg LOCAL_STATIC_LIBRARIES := \ libsparse_host \ libz +LOCAL_CFLAGS := -Werror include $(BUILD_HOST_EXECUTABLE) @@ -95,5 +103,5 @@ LOCAL_MODULE := simg_dump.py LOCAL_SRC_FILES := simg_dump.py LOCAL_MODULE_CLASS := EXECUTABLES LOCAL_IS_HOST_MODULE := true +LOCAL_CFLAGS := -Werror include $(BUILD_PREBUILT) - diff --git a/libnl_2/object.c b/libsparse/defs.h index c53accf..34e63c5 100644 --- a/libnl_2/object.c +++ b/libsparse/defs.h @@ -1,11 +1,11 @@ /* - * Copyright (C) 2011 The Android Open Source Project + * Copyright (C) 2014 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 + * 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, @@ -14,20 +14,10 @@ * limitations under the License. */ -/* NOTICE: This is a clean room re-implementation of libnl */ - -#include "netlink-types.h" - -void nl_object_put(struct nl_object *obj) -{ - obj->ce_refcnt--; - if (!obj->ce_refcnt) - nl_object_free(obj); -} - -void nl_object_free(struct nl_object *obj) -{ - nl_cache_remove(obj); -} +#ifndef _LIBSPARSE_DEFS_H_ +#ifndef __unused +#define __unused __attribute__((__unused__)) +#endif +#endif /* _LIBSPARSE_DEFS_H_ */ diff --git a/libsparse/img2simg.c b/libsparse/img2simg.c index 6b1caa5..a0db36f 100644 --- a/libsparse/img2simg.c +++ b/libsparse/img2simg.c @@ -47,7 +47,6 @@ int main(int argc, char *argv[]) { int in; int out; - unsigned int i; int ret; struct sparse_file *s; unsigned int block_size = 4096; diff --git a/libsparse/include/sparse/sparse.h b/libsparse/include/sparse/sparse.h index 17d085c..8b757d2 100644 --- a/libsparse/include/sparse/sparse.h +++ b/libsparse/include/sparse/sparse.h @@ -20,6 +20,10 @@ #include <stdbool.h> #include <stdint.h> +#ifdef __cplusplus +extern "C" { +#endif + struct sparse_file; /** @@ -273,4 +277,8 @@ void sparse_file_verbose(struct sparse_file *s); */ extern void (*sparse_print_verbose)(const char *fmt, ...); +#ifdef __cplusplus +} +#endif + #endif diff --git a/libsparse/output_file.c b/libsparse/output_file.c index e63c4a9..cd30800 100644 --- a/libsparse/output_file.c +++ b/libsparse/output_file.c @@ -29,9 +29,10 @@ #include <unistd.h> #include <zlib.h> +#include "defs.h" #include "output_file.h" -#include "sparse_format.h" #include "sparse_crc32.h" +#include "sparse_format.h" #ifndef USE_MINGW #include <sys/mman.h> @@ -264,7 +265,7 @@ static struct output_file_ops gz_file_ops = { .close = gz_file_close, }; -static int callback_file_open(struct output_file *out, int fd) +static int callback_file_open(struct output_file *out __unused, int fd __unused) { return 0; } @@ -287,14 +288,13 @@ static int callback_file_skip(struct output_file *out, int64_t off) return 0; } -static int callback_file_pad(struct output_file *out, int64_t len) +static int callback_file_pad(struct output_file *out __unused, int64_t len __unused) { return -1; } static int callback_file_write(struct output_file *out, void *data, int len) { - int ret; struct output_file_callback *outc = to_output_file_callback(out); return outc->write(outc->priv, data, len); @@ -340,7 +340,7 @@ int read_all(int fd, void *buf, size_t len) static int write_sparse_skip_chunk(struct output_file *out, int64_t skip_len) { chunk_header_t chunk_header; - int ret, chunk; + int ret; if (skip_len % out->block_size) { error("don't care size %"PRIi64" is not a multiple of the block size %u", @@ -367,9 +367,8 @@ static int write_sparse_fill_chunk(struct output_file *out, unsigned int len, uint32_t fill_val) { chunk_header_t chunk_header; - int rnd_up_len, zero_len, count; + int rnd_up_len, count; int ret; - unsigned int i; /* Round up the fill length to a multiple of the block size */ rnd_up_len = ALIGN(len, out->block_size); @@ -535,8 +534,6 @@ static struct sparse_file_ops normal_file_ops = { void output_file_close(struct output_file *out) { - int ret; - out->sparse_ops->write_end_chunk(out); out->ops->close(out); } @@ -631,8 +628,8 @@ static struct output_file *output_file_new_normal(void) } struct output_file *output_file_open_callback(int (*write)(void *, const void *, int), - void *priv, unsigned int block_size, int64_t len, int gz, int sparse, - int chunks, int crc) + void *priv, unsigned int block_size, int64_t len, + int gz __unused, int sparse, int chunks, int crc) { int ret; struct output_file_callback *outc; diff --git a/libsparse/sparse.c b/libsparse/sparse.c index 741e8c6..baa30cd 100644 --- a/libsparse/sparse.c +++ b/libsparse/sparse.c @@ -19,6 +19,7 @@ #include <sparse/sparse.h> +#include "defs.h" #include "sparse_file.h" #include "output_file.h" @@ -189,7 +190,7 @@ int sparse_file_callback(struct sparse_file *s, bool sparse, bool crc, return ret; } -static int out_counter_write(void *priv, const void *data, int len) +static int out_counter_write(void *priv, const void *data __unused, int len) { int64_t *count = priv; *count += len; @@ -278,7 +279,6 @@ int sparse_file_resparse(struct sparse_file *in_s, unsigned int max_len, struct sparse_file **out_s, int out_s_count) { struct backed_block *bb; - unsigned int overhead; struct sparse_file *s; struct sparse_file *tmp; int c = 0; diff --git a/libsparse/sparse_read.c b/libsparse/sparse_read.c index 704bcfa..8e188e9 100644 --- a/libsparse/sparse_read.c +++ b/libsparse/sparse_read.c @@ -29,6 +29,8 @@ #include <sparse/sparse.h> +#include "defs.h" +#include "output_file.h" #include "sparse_crc32.h" #include "sparse_file.h" #include "sparse_format.h" @@ -175,24 +177,19 @@ static int process_fill_chunk(struct sparse_file *s, unsigned int chunk_size, } static int process_skip_chunk(struct sparse_file *s, unsigned int chunk_size, - int fd, unsigned int blocks, unsigned int block, uint32_t *crc32) + int fd __unused, unsigned int blocks, + unsigned int block __unused, uint32_t *crc32) { - int ret; - int chunk; - int64_t len = (int64_t)blocks * s->block_size; - uint32_t fill_val; - uint32_t *fillbuf; - unsigned int i; - if (chunk_size != 0) { return -EINVAL; } if (crc32) { + int64_t len = (int64_t)blocks * s->block_size; memset(copybuf, 0, COPY_BUF_SIZE); while (len) { - chunk = min(len, COPY_BUF_SIZE); + int chunk = min(len, COPY_BUF_SIZE); *crc32 = sparse_crc32(*crc32, copybuf, chunk); len -= chunk; } @@ -364,7 +361,6 @@ static int sparse_file_read_normal(struct sparse_file *s, int fd) int64_t remain = s->len; int64_t offset = 0; unsigned int to_read; - char *ptr; unsigned int i; bool sparse_block; diff --git a/libsuspend/Android.mk b/libsuspend/Android.mk index a2fa3e0..1ba2f59 100644 --- a/libsuspend/Android.mk +++ b/libsuspend/Android.mk @@ -18,6 +18,7 @@ LOCAL_MODULE_TAGS := optional LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include LOCAL_C_INCLUDES += $(LOCAL_PATH)/include LOCAL_SHARED_LIBRARIES := $(libsuspend_libraries) +LOCAL_CFLAGS := -Werror #LOCAL_CFLAGS += -DLOG_NDEBUG=0 include $(BUILD_SHARED_LIBRARY) @@ -27,5 +28,6 @@ LOCAL_MODULE := libsuspend LOCAL_MODULE_TAGS := optional LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include LOCAL_C_INCLUDES += $(LOCAL_PATH)/include +LOCAL_CFLAGS := -Werror #LOCAL_CFLAGS += -DLOG_NDEBUG=0 include $(BUILD_STATIC_LIBRARY) diff --git a/libsuspend/autosuspend_earlysuspend.c b/libsuspend/autosuspend_earlysuspend.c index 1df8c6a..2bece4c 100644 --- a/libsuspend/autosuspend_earlysuspend.c +++ b/libsuspend/autosuspend_earlysuspend.c @@ -75,13 +75,8 @@ static int wait_for_fb_sleep(void) return err < 0 ? err : 0; } -static void *earlysuspend_thread_func(void *arg) +static void *earlysuspend_thread_func(void __unused *arg) { - char buf[80]; - char wakeup_count[20]; - int wakeup_count_len; - int ret; - while (1) { if (wait_for_fb_sleep()) { ALOGE("Failed reading wait_for_fb_sleep, exiting earlysuspend thread\n"); diff --git a/libsync/Android.mk b/libsync/Android.mk index 626b762..fd1c88c 100644 --- a/libsync/Android.mk +++ b/libsync/Android.mk @@ -7,6 +7,7 @@ LOCAL_MODULE_TAGS := optional LOCAL_SHARED_LIBRARIES := liblog LOCAL_C_INCLUDES := $(LOCAL_PATH)/include LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include +LOCAL_CFLAGS := -Werror include $(BUILD_SHARED_LIBRARY) include $(CLEAR_VARS) @@ -15,4 +16,5 @@ LOCAL_MODULE := sync_test LOCAL_MODULE_TAGS := optional tests LOCAL_SHARED_LIBRARIES := liblog LOCAL_C_INCLUDES := $(LOCAL_PATH)/include +LOCAL_CFLAGS := -Werror include $(BUILD_EXECUTABLE) diff --git a/libsysutils/Android.mk b/libsysutils/Android.mk index 1451b0d..246f954 100644 --- a/libsysutils/Android.mk +++ b/libsysutils/Android.mk @@ -18,7 +18,7 @@ LOCAL_MODULE:= libsysutils LOCAL_C_INCLUDES := -LOCAL_CFLAGS := +LOCAL_CFLAGS := -Werror LOCAL_SHARED_LIBRARIES := libcutils liblog diff --git a/libsysutils/EventLogTags.logtags b/libsysutils/EventLogTags.logtags index 7aa5cad..713f8cd 100644 --- a/libsysutils/EventLogTags.logtags +++ b/libsysutils/EventLogTags.logtags @@ -1,5 +1,5 @@ # See system/core/logcat/event.logtags for a description of the format of this file. # FrameworkListener dispatchCommand overflow -78001 dispatchCommand_overflow -65537 netlink_failure (uid|1) +78001 exp_det_dispatchCommand_overflow +65537 exp_det_netlink_failure (uid|1) diff --git a/libsysutils/src/FrameworkListener.cpp b/libsysutils/src/FrameworkListener.cpp index a5ffda2..e7b3dd6 100644 --- a/libsysutils/src/FrameworkListener.cpp +++ b/libsysutils/src/FrameworkListener.cpp @@ -39,6 +39,11 @@ FrameworkListener::FrameworkListener(const char *socketName) : init(socketName, false); } +FrameworkListener::FrameworkListener(int sock) : + SocketListener(sock, true) { + init(NULL, false); +} + void FrameworkListener::init(const char *socketName UNUSED, bool withSeq) { mCommands = new FrameworkCommandCollection(); errorRate = 0; @@ -87,7 +92,6 @@ void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) { char *qlimit = tmp + sizeof(tmp) - 1; bool esc = false; bool quote = false; - int k; bool haveCmdNum = !mWithSeq; memset(argv, 0, sizeof(argv)); @@ -161,7 +165,7 @@ void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) { goto overflow; argv[argc++] = strdup(tmp); #if 0 - for (k = 0; k < argc; k++) { + for (int k = 0; k < argc; k++) { SLOGD("arg[%d] = '%s'", k, argv[k]); } #endif diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp index 34f2016..1c9c70a 100644 --- a/libsysutils/src/NetlinkEvent.cpp +++ b/libsysutils/src/NetlinkEvent.cpp @@ -109,7 +109,7 @@ bool NetlinkEvent::parseIfAddrMessage(int type, struct ifaddrmsg *ifaddr, if (ifaddr->ifa_family == AF_INET) { struct in_addr *addr4 = (struct in_addr *) RTA_DATA(rta); if (RTA_PAYLOAD(rta) < sizeof(*addr4)) { - SLOGE("Short IPv4 address (%d bytes) in %s", + SLOGE("Short IPv4 address (%zu bytes) in %s", RTA_PAYLOAD(rta), msgtype); continue; } @@ -117,7 +117,7 @@ bool NetlinkEvent::parseIfAddrMessage(int type, struct ifaddrmsg *ifaddr, } else if (ifaddr->ifa_family == AF_INET6) { struct in6_addr *addr6 = (struct in6_addr *) RTA_DATA(rta); if (RTA_PAYLOAD(rta) < sizeof(*addr6)) { - SLOGE("Short IPv6 address (%d bytes) in %s", + SLOGE("Short IPv6 address (%zu bytes) in %s", RTA_PAYLOAD(rta), msgtype); continue; } @@ -152,7 +152,7 @@ bool NetlinkEvent::parseIfAddrMessage(int type, struct ifaddrmsg *ifaddr, } if (RTA_PAYLOAD(rta) < sizeof(*cacheinfo)) { - SLOGE("Short IFA_CACHEINFO (%d vs. %d bytes) in %s", + SLOGE("Short IFA_CACHEINFO (%zu vs. %zu bytes) in %s", RTA_PAYLOAD(rta), sizeof(cacheinfo), msgtype); continue; } @@ -174,7 +174,6 @@ bool NetlinkEvent::parseIfAddrMessage(int type, struct ifaddrmsg *ifaddr, } /* -<<<<<<< HEAD * Parse a RTM_NEWNDUSEROPT message. */ bool NetlinkEvent::parseNdUserOptMessage(struct nduseroptmsg *msg, int len) { @@ -399,7 +398,6 @@ bool NetlinkEvent::parseAsciiNetlinkMessage(char *buffer, int size) { const char *s = buffer; const char *end; int param_idx = 0; - int i; int first = 1; if (size == 0) diff --git a/libsysutils/src/SocketClient.cpp b/libsysutils/src/SocketClient.cpp index 3625d93..d3ce8f5 100644 --- a/libsysutils/src/SocketClient.cpp +++ b/libsysutils/src/SocketClient.cpp @@ -1,10 +1,11 @@ #include <alloca.h> #include <errno.h> -#include <sys/socket.h> -#include <sys/types.h> #include <pthread.h> +#include <signal.h> #include <string.h> #include <arpa/inet.h> +#include <sys/socket.h> +#include <sys/types.h> #define LOG_TAG "SocketClient" #include <cutils/log.h> @@ -43,8 +44,7 @@ void SocketClient::init(int socket, bool owned, bool useCmdNum) { } } -SocketClient::~SocketClient() -{ +SocketClient::~SocketClient() { if (mSocketOwned) { close(mSocket); } @@ -180,10 +180,19 @@ int SocketClient::sendDataLockedv(struct iovec *iov, int iovcnt) { return 0; } + int ret = 0; + int e = 0; // SLOGW and sigaction are not inert regarding errno int current = 0; + struct sigaction new_action, old_action; + memset(&new_action, 0, sizeof(new_action)); + new_action.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &new_action, &old_action); + for (;;) { - ssize_t rc = writev(mSocket, iov + current, iovcnt - current); + ssize_t rc = TEMP_FAILURE_RETRY( + writev(mSocket, iov + current, iovcnt - current)); + if (rc > 0) { size_t written = rc; while ((current < iovcnt) && (written >= iov[current].iov_len)) { @@ -198,18 +207,21 @@ int SocketClient::sendDataLockedv(struct iovec *iov, int iovcnt) { continue; } - if (rc < 0 && errno == EINTR) - continue; - if (rc == 0) { + e = EIO; SLOGW("0 length write :("); - errno = EIO; } else { - SLOGW("write error (%s)", strerror(errno)); + e = errno; + SLOGW("write error (%s)", strerror(e)); } - return -1; + ret = -1; + break; } - return 0; + + sigaction(SIGPIPE, &old_action, &new_action); + + errno = e; + return ret; } void SocketClient::incRef() { diff --git a/libsysutils/src/SocketListener.cpp b/libsysutils/src/SocketListener.cpp index 5c75206..527a6a0 100644 --- a/libsysutils/src/SocketListener.cpp +++ b/libsysutils/src/SocketListener.cpp @@ -70,6 +70,10 @@ SocketListener::~SocketListener() { } int SocketListener::startListener() { + return startListener(4); +} + +int SocketListener::startListener(int backlog) { if (!mSocketName && mSock == -1) { SLOGE("Failed to start unbound listener"); @@ -84,7 +88,7 @@ int SocketListener::startListener() { SLOGV("got mSock = %d for %s", mSock, mSocketName); } - if (mListen && listen(mSock, 4) < 0) { + if (mListen && listen(mSock, backlog) < 0) { SLOGE("Unable to listen on socket (%s)", strerror(errno)); return -1; } else if (!mListen) diff --git a/libusbhost/Android.mk b/libusbhost/Android.mk index 9565cc5..5c12f2c 100644 --- a/libusbhost/Android.mk +++ b/libusbhost/Android.mk @@ -14,7 +14,7 @@ # limitations under the License. # -LOCAL_PATH := $(my-dir) +LOCAL_PATH := $(call my-dir) # Static library for Linux host # ======================================================== @@ -25,6 +25,7 @@ include $(CLEAR_VARS) LOCAL_MODULE := libusbhost LOCAL_SRC_FILES := usbhost.c +LOCAL_CFLAGS := -Werror include $(BUILD_HOST_STATIC_LIBRARY) @@ -38,7 +39,7 @@ include $(CLEAR_VARS) LOCAL_MODULE := libusbhost LOCAL_SRC_FILES := usbhost.c -LOCAL_CFLAGS := -g -DUSE_LIBLOG +LOCAL_CFLAGS := -g -DUSE_LIBLOG -Werror # needed for logcat LOCAL_SHARED_LIBRARIES := libcutils @@ -52,5 +53,6 @@ include $(CLEAR_VARS) LOCAL_MODULE := libusbhost LOCAL_SRC_FILES := usbhost.c +LOCAL_CFLAGS := -Werror include $(BUILD_STATIC_LIBRARY) diff --git a/libusbhost/usbhost.c b/libusbhost/usbhost.c index 8be393e..cd8000a 100644 --- a/libusbhost/usbhost.c +++ b/libusbhost/usbhost.c @@ -263,11 +263,12 @@ int usb_host_read_event(struct usb_host_context *context) D("%s subdirectory %s: index: %d\n", (event->mask & IN_CREATE) ? "new" : "gone", path, i); if (i > 0 && i < MAX_USBFS_WD_COUNT) { + int local_ret = 0; if (event->mask & IN_CREATE) { - ret = inotify_add_watch(context->fd, path, + local_ret = inotify_add_watch(context->fd, path, IN_CREATE | IN_DELETE); - if (ret >= 0) - context->wds[i] = ret; + if (local_ret >= 0) + context->wds[i] = local_ret; done = find_existing_devices_bus(path, context->cb_added, context->data); } else if (event->mask & IN_DELETE) { diff --git a/libutils/Android.mk b/libutils/Android.mk index 1710d36..9a50147 100644 --- a/libutils/Android.mk +++ b/libutils/Android.mk @@ -43,7 +43,7 @@ commonSources:= \ VectorImpl.cpp \ misc.cpp -host_commonCflags := -DLIBUTILS_NATIVE=1 $(TOOL_CFLAGS) +host_commonCflags := -DLIBUTILS_NATIVE=1 $(TOOL_CFLAGS) -Werror ifeq ($(HOST_OS),windows) ifeq ($(strip $(USE_CYGWIN),),) @@ -69,7 +69,7 @@ endif LOCAL_MODULE:= libutils LOCAL_STATIC_LIBRARIES := liblog LOCAL_CFLAGS += $(host_commonCflags) -LOCAL_LDLIBS += $(host_commonLdlibs) +LOCAL_MULTILIB := both include $(BUILD_HOST_STATIC_LIBRARY) @@ -83,7 +83,6 @@ endif LOCAL_MODULE:= lib64utils LOCAL_STATIC_LIBRARIES := liblog LOCAL_CFLAGS += $(host_commonCflags) -m64 -LOCAL_LDLIBS += $(host_commonLdlibs) include $(BUILD_HOST_STATIC_LIBRARY) @@ -98,20 +97,15 @@ LOCAL_SRC_FILES:= \ Looper.cpp \ Trace.cpp -ifeq ($(TARGET_OS),linux) -LOCAL_LDLIBS += -lrt -ldl -endif - ifeq ($(TARGET_ARCH),mips) LOCAL_CFLAGS += -DALIGN_DOUBLE endif +LOCAL_CFLAGS += -Werror LOCAL_C_INCLUDES += \ bionic/libc/private \ external/zlib -LOCAL_LDLIBS += -lpthread - LOCAL_STATIC_LIBRARIES := \ libcutils @@ -134,7 +128,8 @@ LOCAL_SHARED_LIBRARIES := \ libbacktrace \ libcutils \ libdl \ - liblog \ + liblog +LOCAL_CFLAGS := -Werror include external/stlport/libstlport.mk diff --git a/libutils/BlobCache.cpp b/libutils/BlobCache.cpp index 0fb1d8e..f00bf14 100644 --- a/libutils/BlobCache.cpp +++ b/libutils/BlobCache.cpp @@ -17,6 +17,7 @@ #define LOG_TAG "BlobCache" //#define LOG_NDEBUG 0 +#include <inttypes.h> #include <stdlib.h> #include <string.h> @@ -27,7 +28,7 @@ namespace android { // BlobCache::Header::mMagicNumber value -static const uint32_t blobCacheMagic = '_Bb$'; +static const uint32_t blobCacheMagic = ('_' << 24) + ('B' << 16) + ('b' << 8) + '$'; // BlobCache::Header::mBlobCacheVersion value static const uint32_t blobCacheVersion = 1; @@ -48,24 +49,24 @@ BlobCache::BlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize mRandState[1] = (now >> 16) & 0xFFFF; mRandState[2] = (now >> 32) & 0xFFFF; #endif - ALOGV("initializing random seed using %lld", now); + ALOGV("initializing random seed using %lld", (unsigned long long)now); } void BlobCache::set(const void* key, size_t keySize, const void* value, size_t valueSize) { if (mMaxKeySize < keySize) { - ALOGV("set: not caching because the key is too large: %d (limit: %d)", + ALOGV("set: not caching because the key is too large: %zu (limit: %zu)", keySize, mMaxKeySize); return; } if (mMaxValueSize < valueSize) { - ALOGV("set: not caching because the value is too large: %d (limit: %d)", + ALOGV("set: not caching because the value is too large: %zu (limit: %zu)", valueSize, mMaxValueSize); return; } if (mMaxTotalSize < keySize + valueSize) { ALOGV("set: not caching because the combined key/value size is too " - "large: %d (limit: %d)", keySize + valueSize, mMaxTotalSize); + "large: %zu (limit: %zu)", keySize + valueSize, mMaxTotalSize); return; } if (keySize == 0) { @@ -94,15 +95,15 @@ void BlobCache::set(const void* key, size_t keySize, const void* value, continue; } else { ALOGV("set: not caching new key/value pair because the " - "total cache size limit would be exceeded: %d " - "(limit: %d)", + "total cache size limit would be exceeded: %zu " + "(limit: %zu)", keySize + valueSize, mMaxTotalSize); break; } } mCacheEntries.add(CacheEntry(keyBlob, valueBlob)); mTotalSize = newTotalSize; - ALOGV("set: created new cache entry with %d byte key and %d byte value", + ALOGV("set: created new cache entry with %zu byte key and %zu byte value", keySize, valueSize); } else { // Update the existing cache entry. @@ -116,14 +117,14 @@ void BlobCache::set(const void* key, size_t keySize, const void* value, continue; } else { ALOGV("set: not caching new value because the total cache " - "size limit would be exceeded: %d (limit: %d)", + "size limit would be exceeded: %zu (limit: %zu)", keySize + valueSize, mMaxTotalSize); break; } } mCacheEntries.editItemAt(index).setValue(valueBlob); mTotalSize = newTotalSize; - ALOGV("set: updated existing cache entry with %d byte key and %d byte " + ALOGV("set: updated existing cache entry with %zu byte key and %zu byte " "value", keySize, valueSize); } break; @@ -133,7 +134,7 @@ void BlobCache::set(const void* key, size_t keySize, const void* value, size_t BlobCache::get(const void* key, size_t keySize, void* value, size_t valueSize) { if (mMaxKeySize < keySize) { - ALOGV("get: not searching because the key is too large: %d (limit %d)", + ALOGV("get: not searching because the key is too large: %zu (limit %zu)", keySize, mMaxKeySize); return 0; } @@ -141,7 +142,7 @@ size_t BlobCache::get(const void* key, size_t keySize, void* value, CacheEntry dummyEntry(dummyKey, NULL); ssize_t index = mCacheEntries.indexOf(dummyEntry); if (index < 0) { - ALOGV("get: no cache entry found for key of size %d", keySize); + ALOGV("get: no cache entry found for key of size %zu", keySize); return 0; } @@ -150,10 +151,10 @@ size_t BlobCache::get(const void* key, size_t keySize, void* value, sp<Blob> valueBlob(mCacheEntries[index].getValue()); size_t valueBlobSize = valueBlob->getSize(); if (valueBlobSize <= valueSize) { - ALOGV("get: copying %d bytes to caller's buffer", valueBlobSize); + ALOGV("get: copying %zu bytes to caller's buffer", valueBlobSize); memcpy(value, valueBlob->getData(), valueBlobSize); } else { - ALOGV("get: caller's buffer is too small for value: %d (needs %d)", + ALOGV("get: caller's buffer is too small for value: %zu (needs %zu)", valueSize, valueBlobSize); } return valueBlobSize; @@ -229,7 +230,7 @@ status_t BlobCache::unflatten(void const* buffer, size_t size) { } const Header* header = reinterpret_cast<const Header*>(buffer); if (header->mMagicNumber != blobCacheMagic) { - ALOGE("unflatten: bad magic number: %d", header->mMagicNumber); + ALOGE("unflatten: bad magic number: %" PRIu32, header->mMagicNumber); return BAD_VALUE; } if (header->mBlobCacheVersion != blobCacheVersion || diff --git a/libutils/FileMap.cpp b/libutils/FileMap.cpp index 9ce370e..be4b14f 100644 --- a/libutils/FileMap.cpp +++ b/libutils/FileMap.cpp @@ -23,6 +23,13 @@ #include <utils/FileMap.h> #include <utils/Log.h> +#if defined(HAVE_WIN32_FILEMAP) && !defined(__USE_MINGW_ANSI_STDIO) +# define PRId32 "I32d" +# define PRIx32 "I32x" +# define PRId64 "I64d" +#else +#include <inttypes.h> +#endif #include <stdio.h> #include <stdlib.h> @@ -39,37 +46,32 @@ using namespace android; /*static*/ long FileMap::mPageSize = -1; - -/* - * Constructor. Create an empty object. - */ +// Constructor. Create an empty object. FileMap::FileMap(void) : mRefCount(1), mFileName(NULL), mBasePtr(NULL), mBaseLength(0), mDataPtr(NULL), mDataLength(0) { } -/* - * Destructor. - */ +// Destructor. FileMap::~FileMap(void) { assert(mRefCount == 0); - //printf("+++ removing FileMap %p %u\n", mDataPtr, mDataLength); + //printf("+++ removing FileMap %p %zu\n", mDataPtr, mDataLength); mRefCount = -100; // help catch double-free if (mFileName != NULL) { free(mFileName); } -#ifdef HAVE_POSIX_FILEMAP +#ifdef HAVE_POSIX_FILEMAP if (mBasePtr && munmap(mBasePtr, mBaseLength) != 0) { - ALOGD("munmap(%p, %d) failed\n", mBasePtr, (int) mBaseLength); + ALOGD("munmap(%p, %zu) failed\n", mBasePtr, mBaseLength); } #endif #ifdef HAVE_WIN32_FILEMAP if (mBasePtr && UnmapViewOfFile(mBasePtr) == 0) { - ALOGD("UnmapViewOfFile(%p) failed, error = %ld\n", mBasePtr, + ALOGD("UnmapViewOfFile(%p) failed, error = %" PRId32 "\n", mBasePtr, GetLastError() ); } if (mFileMapping != INVALID_HANDLE_VALUE) { @@ -80,14 +82,12 @@ FileMap::~FileMap(void) } -/* - * Create a new mapping on an open file. - * - * Closing the file descriptor does not unmap the pages, so we don't - * claim ownership of the fd. - * - * Returns "false" on failure. - */ +// Create a new mapping on an open file. +// +// Closing the file descriptor does not unmap the pages, so we don't +// claim ownership of the fd. +// +// Returns "false" on failure. bool FileMap::create(const char* origFileName, int fd, off64_t offset, size_t length, bool readOnly) { @@ -98,32 +98,32 @@ bool FileMap::create(const char* origFileName, int fd, off64_t offset, size_t le if (mPageSize == -1) { SYSTEM_INFO si; - + GetSystemInfo( &si ); mPageSize = si.dwAllocationGranularity; } DWORD protect = readOnly ? PAGE_READONLY : PAGE_READWRITE; - + mFileHandle = (HANDLE) _get_osfhandle(fd); mFileMapping = CreateFileMapping( mFileHandle, NULL, protect, 0, 0, NULL); if (mFileMapping == NULL) { - ALOGE("CreateFileMapping(%p, %lx) failed with error %ld\n", + ALOGE("CreateFileMapping(%p, %" PRIx32 ") failed with error %" PRId32 "\n", mFileHandle, protect, GetLastError() ); return false; } - + adjust = offset % mPageSize; adjOffset = offset - adjust; adjLength = length + adjust; - - mBasePtr = MapViewOfFile( mFileMapping, + + mBasePtr = MapViewOfFile( mFileMapping, readOnly ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS, 0, (DWORD)(adjOffset), adjLength ); if (mBasePtr == NULL) { - ALOGE("MapViewOfFile(%ld, %ld) failed with error %ld\n", + ALOGE("MapViewOfFile(%" PRId64 ", %zu) failed with error %" PRId32 "\n", adjOffset, adjLength, GetLastError() ); CloseHandle(mFileMapping); mFileMapping = INVALID_HANDLE_VALUE; @@ -142,7 +142,7 @@ bool FileMap::create(const char* origFileName, int fd, off64_t offset, size_t le assert(offset >= 0); assert(length > 0); - /* init on first use */ + // init on first use if (mPageSize == -1) { #if NOT_USING_KLIBC mPageSize = sysconf(_SC_PAGESIZE); @@ -151,7 +151,7 @@ bool FileMap::create(const char* origFileName, int fd, off64_t offset, size_t le return false; } #else - /* this holds for Linux, Darwin, Cygwin, and doesn't pain the ARM */ + // this holds for Linux, Darwin, Cygwin, and doesn't pain the ARM mPageSize = 4096; #endif } @@ -168,19 +168,19 @@ try_again: ptr = mmap(NULL, adjLength, prot, flags, fd, adjOffset); if (ptr == MAP_FAILED) { - // Cygwin does not seem to like file mapping files from an offset. - // So if we fail, try again with offset zero - if (adjOffset > 0) { - adjust = offset; - goto try_again; - } - - ALOGE("mmap(%ld,%ld) failed: %s\n", - (long) adjOffset, (long) adjLength, strerror(errno)); + // Cygwin does not seem to like file mapping files from an offset. + // So if we fail, try again with offset zero + if (adjOffset > 0) { + adjust = offset; + goto try_again; + } + + ALOGE("mmap(%lld,%zu) failed: %s\n", + (long long)adjOffset, adjLength, strerror(errno)); return false; } mBasePtr = ptr; -#endif /* HAVE_POSIX_FILEMAP */ +#endif // HAVE_POSIX_FILEMAP mFileName = origFileName != NULL ? strdup(origFileName) : NULL; mBaseLength = adjLength; @@ -190,15 +190,13 @@ try_again: assert(mBasePtr != NULL); - ALOGV("MAP: base %p/%d data %p/%d\n", - mBasePtr, (int) mBaseLength, mDataPtr, (int) mDataLength); + ALOGV("MAP: base %p/%zu data %p/%zu\n", + mBasePtr, mBaseLength, mDataPtr, mDataLength); return true; } -/* - * Provide guidance to the system. - */ +// Provide guidance to the system. int FileMap::advise(MapAdvice advice) { #if HAVE_MADVISE @@ -220,6 +218,6 @@ int FileMap::advise(MapAdvice advice) ALOGW("madvise(%d) failed: %s\n", sysAdvice, strerror(errno)); return cc; #else - return -1; + return -1; #endif // HAVE_MADVISE } diff --git a/libutils/LinearAllocator.cpp b/libutils/LinearAllocator.cpp index a07a291..8b90696 100644 --- a/libutils/LinearAllocator.cpp +++ b/libutils/LinearAllocator.cpp @@ -92,7 +92,7 @@ public: : mNextPage(0) {} - void* operator new(size_t size, void* buf) { return buf; } + void* operator new(size_t /*size*/, void* buf) { return buf; } void* start() { return (void*) (((size_t)this) + sizeof(Page)); @@ -103,7 +103,7 @@ public: } private: - Page(const Page& other) {} + Page(const Page& /*other*/) {} Page* mNextPage; }; diff --git a/libutils/Printer.cpp b/libutils/Printer.cpp index 263e740..1dc8632 100644 --- a/libutils/Printer.cpp +++ b/libutils/Printer.cpp @@ -25,10 +25,6 @@ #include <stdio.h> #include <stdlib.h> -#ifndef __BIONIC__ -#define fdprintf dprintf -#endif - namespace android { /* @@ -120,7 +116,7 @@ void FdPrinter::printLine(const char* string) { } #ifndef USE_MINGW - fdprintf(mFd, mFormatString, mPrefix, string); + dprintf(mFd, mFormatString, mPrefix, string); #endif } diff --git a/libutils/RefBase.cpp b/libutils/RefBase.cpp index f398a82..02907ad 100644 --- a/libutils/RefBase.cpp +++ b/libutils/RefBase.cpp @@ -17,6 +17,14 @@ #define LOG_TAG "RefBase" // #define LOG_NDEBUG 0 +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <typeinfo> +#include <unistd.h> + #include <utils/RefBase.h> #include <utils/Atomic.h> @@ -24,13 +32,9 @@ #include <utils/Log.h> #include <utils/threads.h> -#include <stdlib.h> -#include <stdio.h> -#include <typeinfo> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <unistd.h> +#ifndef __unused +#define __unused __attribute__((__unused__)) +#endif // compile with refcounting debugging enabled #define DEBUG_REFS 0 @@ -109,7 +113,7 @@ public: char inc = refs->ref >= 0 ? '+' : '-'; ALOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref); #if DEBUG_REFS_CALLSTACK_ENABLED - refs->stack.dump(LOG_TAG); + refs->stack.log(LOG_TAG); #endif refs = refs->next; } @@ -123,7 +127,7 @@ public: char inc = refs->ref >= 0 ? '+' : '-'; ALOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref); #if DEBUG_REFS_CALLSTACK_ENABLED - refs->stack.dump(LOG_TAG); + refs->stack.log(LOG_TAG); #endif refs = refs->next; } @@ -388,7 +392,7 @@ void RefBase::weakref_type::incWeak(const void* id) { weakref_impl* const impl = static_cast<weakref_impl*>(this); impl->addWeakRef(id); - const int32_t c = android_atomic_inc(&impl->mWeak); + const int32_t c __unused = android_atomic_inc(&impl->mWeak); ALOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this); } @@ -615,7 +619,7 @@ void RefBase::onLastStrongRef(const void* /*id*/) { } -bool RefBase::onIncStrongAttempted(uint32_t flags, const void* id) +bool RefBase::onIncStrongAttempted(uint32_t flags, const void* /*id*/) { return (flags&FIRST_INC_STRONG) ? true : false; } @@ -626,13 +630,15 @@ void RefBase::onLastWeakRef(const void* /*id*/) // --------------------------------------------------------------------------- -void RefBase::renameRefs(size_t n, const ReferenceRenamer& renamer) { #if DEBUG_REFS +void RefBase::renameRefs(size_t n, const ReferenceRenamer& renamer) { for (size_t i=0 ; i<n ; i++) { renamer(i); } -#endif } +#else +void RefBase::renameRefs(size_t /*n*/, const ReferenceRenamer& /*renamer*/) { } +#endif void RefBase::renameRefId(weakref_type* ref, const void* old_id, const void* new_id) { diff --git a/libutils/StopWatch.cpp b/libutils/StopWatch.cpp index b1708d6..8c7b596 100644 --- a/libutils/StopWatch.cpp +++ b/libutils/StopWatch.cpp @@ -21,7 +21,9 @@ #include <stdio.h> /* for PRId64 */ +#ifndef __STDC_FORMAT_MACROS #define __STDC_FORMAT_MACROS 1 +#endif #include <inttypes.h> #include <utils/Log.h> diff --git a/libutils/String16.cpp b/libutils/String16.cpp index b09b728..91efdaa 100644 --- a/libutils/String16.cpp +++ b/libutils/String16.cpp @@ -16,7 +16,6 @@ #include <utils/String16.h> -#include <utils/Debug.h> #include <utils/Log.h> #include <utils/Unicode.h> #include <utils/String8.h> @@ -67,8 +66,6 @@ static char16_t* allocFromUTF8(const char* u8str, size_t u8len) return getEmptyString(); } - const uint8_t* const u8end = u8cur + u8len; - SharedBuffer* buf = SharedBuffer::alloc(sizeof(char16_t)*(u16len+1)); if (buf) { u8cur = (const uint8_t*) u8str; diff --git a/libutils/String8.cpp b/libutils/String8.cpp index e852d77..49340bb 100644 --- a/libutils/String8.cpp +++ b/libutils/String8.cpp @@ -323,8 +323,17 @@ status_t String8::appendFormat(const char* fmt, ...) status_t String8::appendFormatV(const char* fmt, va_list args) { - int result = NO_ERROR; - int n = vsnprintf(NULL, 0, fmt, args); + int n, result = NO_ERROR; + va_list tmp_args; + + /* args is undefined after vsnprintf. + * So we need a copy here to avoid the + * second vsnprintf access undefined args. + */ + va_copy(tmp_args, args); + n = vsnprintf(NULL, 0, fmt, tmp_args); + va_end(tmp_args); + if (n != 0) { size_t oldLength = length(); char* buf = lockBuffer(oldLength + n); @@ -542,7 +551,6 @@ char* String8::find_extension(void) const { const char* lastSlash; const char* lastDot; - int extLen; const char* const str = mString; // only look at the filename diff --git a/libutils/SystemClock.cpp b/libutils/SystemClock.cpp index 413250f..dbad581 100644 --- a/libutils/SystemClock.cpp +++ b/libutils/SystemClock.cpp @@ -68,13 +68,7 @@ int64_t elapsedRealtime() */ #define DEBUG_TIMESTAMP 0 -static const char *gettime_method_names[] = { - "clock_gettime", - "ioctl", - "systemTime", -}; - -#if DEBUG_TIMESTAMP +#if DEBUG_TIMESTAMP && defined(ARCH_ARM) static inline void checkTimeStamps(int64_t timestamp, int64_t volatile *prevTimestampPtr, int volatile *prevMethodPtr, @@ -85,11 +79,16 @@ static inline void checkTimeStamps(int64_t timestamp, * gettid, and int64_t is different on the ARM platform * (ie long vs long long). */ -#ifdef ARCH_ARM int64_t prevTimestamp = *prevTimestampPtr; int prevMethod = *prevMethodPtr; if (timestamp < prevTimestamp) { + static const char *gettime_method_names[] = { + "clock_gettime", + "ioctl", + "systemTime", + }; + ALOGW("time going backwards: prev %lld(%s) vs now %lld(%s), tid=%d", prevTimestamp, gettime_method_names[prevMethod], timestamp, gettime_method_names[curMethod], @@ -99,7 +98,6 @@ static inline void checkTimeStamps(int64_t timestamp, // write is interrupted or not observed as a whole. *prevTimestampPtr = timestamp; *prevMethodPtr = curMethod; -#endif } #else #define checkTimeStamps(timestamp, prevTimestampPtr, prevMethodPtr, curMethod) diff --git a/libutils/Threads.cpp b/libutils/Threads.cpp index ff74914..cc7fe89 100644 --- a/libutils/Threads.cpp +++ b/libutils/Threads.cpp @@ -17,16 +17,11 @@ // #define LOG_NDEBUG 0 #define LOG_TAG "libutils.threads" -#include <utils/threads.h> -#include <utils/Log.h> - -#include <cutils/sched_policy.h> - +#include <assert.h> +#include <errno.h> +#include <memory.h> #include <stdio.h> #include <stdlib.h> -#include <memory.h> -#include <errno.h> -#include <assert.h> #include <unistd.h> #if defined(HAVE_PTHREADS) @@ -47,6 +42,17 @@ #include <sys/prctl.h> #endif +#include <utils/threads.h> +#include <utils/Log.h> + +#include <cutils/sched_policy.h> + +#ifdef HAVE_ANDROID_OS +# define __android_unused +#else +# define __android_unused __attribute__((__unused__)) +#endif + /* * =========================================================================== * Thread wrappers @@ -119,7 +125,7 @@ void androidSetThreadName(const char* name) { int androidCreateRawThreadEtc(android_thread_func_t entryFunction, void *userData, - const char* threadName, + const char* threadName __android_unused, int32_t threadPriority, size_t threadStackSize, android_thread_id_t *threadId) @@ -251,9 +257,9 @@ static bool doCreateThread(android_thread_func_t fn, void* arg, android_thread_i int androidCreateRawThreadEtc(android_thread_func_t fn, void *userData, - const char* threadName, - int32_t threadPriority, - size_t threadStackSize, + const char* /*threadName*/, + int32_t /*threadPriority*/, + size_t /*threadStackSize*/, android_thread_id_t *threadId) { return doCreateThread( fn, userData, threadId); diff --git a/libutils/Timers.cpp b/libutils/Timers.cpp index 5293cd2..4687d4d 100644 --- a/libutils/Timers.cpp +++ b/libutils/Timers.cpp @@ -32,9 +32,9 @@ #include <windows.h> #endif +#if defined(HAVE_ANDROID_OS) nsecs_t systemTime(int clock) { -#if defined(HAVE_POSIX_CLOCKS) static const clockid_t clocks[] = { CLOCK_REALTIME, CLOCK_MONOTONIC, @@ -46,14 +46,19 @@ nsecs_t systemTime(int clock) t.tv_sec = t.tv_nsec = 0; clock_gettime(clocks[clock], &t); return nsecs_t(t.tv_sec)*1000000000LL + t.tv_nsec; +} #else - // we don't support the clocks here. +nsecs_t systemTime(int /*clock*/) +{ + // Clock support varies widely across hosts. Mac OS doesn't support + // posix clocks, older glibcs don't support CLOCK_BOOTTIME and Windows + // is windows. struct timeval t; t.tv_sec = t.tv_usec = 0; gettimeofday(&t, NULL); return nsecs_t(t.tv_sec)*1000000000LL + nsecs_t(t.tv_usec)*1000LL; -#endif } +#endif int toMillisecondTimeoutDelay(nsecs_t referenceTime, nsecs_t timeoutTime) { diff --git a/libutils/Unicode.cpp b/libutils/Unicode.cpp index a66e3bb..fe8887d 100644 --- a/libutils/Unicode.cpp +++ b/libutils/Unicode.cpp @@ -576,7 +576,7 @@ void utf8_to_utf16(const uint8_t* u8str, size_t u8len, char16_t* u16str) { char16_t* utf8_to_utf16_n(const uint8_t* src, size_t srcLen, char16_t* dst, size_t dstLen) { const uint8_t* const u8end = src + srcLen; const uint8_t* u8cur = src; - const uint16_t* const u16end = dst + dstLen; + const char16_t* const u16end = dst + dstLen; char16_t* u16cur = dst; while (u8cur < u8end && u16cur < u16end) { diff --git a/libutils/tests/BasicHashtable_test.cpp b/libutils/tests/BasicHashtable_test.cpp index 7dcf750..a61b1e1 100644 --- a/libutils/tests/BasicHashtable_test.cpp +++ b/libutils/tests/BasicHashtable_test.cpp @@ -397,7 +397,7 @@ TEST_F(BasicHashtableTest, Next_WhenNonEmpty_IteratesOverAllEntries) { const SimpleEntry& entry = h.entryAt(index); ASSERT_GE(entry.key, 0); ASSERT_LT(entry.key, N); - ASSERT_EQ(false, set[entry.key]); + ASSERT_FALSE(set[entry.key]); ASSERT_EQ(entry.key * 10, entry.value); set[entry.key] = true; diff --git a/libutils/tests/BlobCache_test.cpp b/libutils/tests/BlobCache_test.cpp index 7202123..dac4e2c 100644 --- a/libutils/tests/BlobCache_test.cpp +++ b/libutils/tests/BlobCache_test.cpp @@ -44,7 +44,7 @@ protected: }; TEST_F(BlobCacheTest, CacheSingleValueSucceeds) { - char buf[4] = { 0xee, 0xee, 0xee, 0xee }; + unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee }; mBC->set("abcd", 4, "efgh", 4); ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4)); ASSERT_EQ('e', buf[0]); @@ -54,7 +54,7 @@ TEST_F(BlobCacheTest, CacheSingleValueSucceeds) { } TEST_F(BlobCacheTest, CacheTwoValuesSucceeds) { - char buf[2] = { 0xee, 0xee }; + unsigned char buf[2] = { 0xee, 0xee }; mBC->set("ab", 2, "cd", 2); mBC->set("ef", 2, "gh", 2); ASSERT_EQ(size_t(2), mBC->get("ab", 2, buf, 2)); @@ -66,7 +66,7 @@ TEST_F(BlobCacheTest, CacheTwoValuesSucceeds) { } TEST_F(BlobCacheTest, GetOnlyWritesInsideBounds) { - char buf[6] = { 0xee, 0xee, 0xee, 0xee, 0xee, 0xee }; + unsigned char buf[6] = { 0xee, 0xee, 0xee, 0xee, 0xee, 0xee }; mBC->set("abcd", 4, "efgh", 4); ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf+1, 4)); ASSERT_EQ(0xee, buf[0]); @@ -78,7 +78,7 @@ TEST_F(BlobCacheTest, GetOnlyWritesInsideBounds) { } TEST_F(BlobCacheTest, GetOnlyWritesIfBufferIsLargeEnough) { - char buf[3] = { 0xee, 0xee, 0xee }; + unsigned char buf[3] = { 0xee, 0xee, 0xee }; mBC->set("abcd", 4, "efgh", 4); ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 3)); ASSERT_EQ(0xee, buf[0]); @@ -92,7 +92,7 @@ TEST_F(BlobCacheTest, GetDoesntAccessNullBuffer) { } TEST_F(BlobCacheTest, MultipleSetsCacheLatestValue) { - char buf[4] = { 0xee, 0xee, 0xee, 0xee }; + unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee }; mBC->set("abcd", 4, "efgh", 4); mBC->set("abcd", 4, "ijkl", 4); ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4)); @@ -103,7 +103,7 @@ TEST_F(BlobCacheTest, MultipleSetsCacheLatestValue) { } TEST_F(BlobCacheTest, SecondSetKeepsFirstValueIfTooLarge) { - char buf[MAX_VALUE_SIZE+1] = { 0xee, 0xee, 0xee, 0xee }; + unsigned char buf[MAX_VALUE_SIZE+1] = { 0xee, 0xee, 0xee, 0xee }; mBC->set("abcd", 4, "efgh", 4); mBC->set("abcd", 4, buf, MAX_VALUE_SIZE+1); ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4)); @@ -115,7 +115,7 @@ TEST_F(BlobCacheTest, SecondSetKeepsFirstValueIfTooLarge) { TEST_F(BlobCacheTest, DoesntCacheIfKeyIsTooBig) { char key[MAX_KEY_SIZE+1]; - char buf[4] = { 0xee, 0xee, 0xee, 0xee }; + unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee }; for (int i = 0; i < MAX_KEY_SIZE+1; i++) { key[i] = 'a'; } @@ -165,7 +165,7 @@ TEST_F(BlobCacheTest, DoesntCacheIfKeyValuePairIsTooBig) { TEST_F(BlobCacheTest, CacheMaxKeySizeSucceeds) { char key[MAX_KEY_SIZE]; - char buf[4] = { 0xee, 0xee, 0xee, 0xee }; + unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee }; for (int i = 0; i < MAX_KEY_SIZE; i++) { key[i] = 'a'; } @@ -214,7 +214,7 @@ TEST_F(BlobCacheTest, CacheMaxKeyValuePairSizeSucceeds) { } TEST_F(BlobCacheTest, CacheMinKeyAndValueSizeSucceeds) { - char buf[1] = { 0xee }; + unsigned char buf[1] = { 0xee }; mBC->set("x", 1, "y", 1); ASSERT_EQ(size_t(1), mBC->get("x", 1, buf, 1)); ASSERT_EQ('y', buf[0]); @@ -282,7 +282,7 @@ protected: }; TEST_F(BlobCacheFlattenTest, FlattenOneValue) { - char buf[4] = { 0xee, 0xee, 0xee, 0xee }; + unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee }; mBC->set("abcd", 4, "efgh", 4); roundTrip(); ASSERT_EQ(size_t(4), mBC2->get("abcd", 4, buf, 4)); @@ -348,7 +348,7 @@ TEST_F(BlobCacheFlattenTest, FlattenCatchesBufferTooSmall) { } TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadMagic) { - char buf[4] = { 0xee, 0xee, 0xee, 0xee }; + unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee }; mBC->set("abcd", 4, "efgh", 4); size_t size = mBC->getFlattenedSize(); @@ -365,7 +365,7 @@ TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadMagic) { } TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheVersion) { - char buf[4] = { 0xee, 0xee, 0xee, 0xee }; + unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee }; mBC->set("abcd", 4, "efgh", 4); size_t size = mBC->getFlattenedSize(); @@ -384,7 +384,7 @@ TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheVersion) { } TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheDeviceVersion) { - char buf[4] = { 0xee, 0xee, 0xee, 0xee }; + unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee }; mBC->set("abcd", 4, "efgh", 4); size_t size = mBC->getFlattenedSize(); @@ -403,7 +403,7 @@ TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheDeviceVersion) { } TEST_F(BlobCacheFlattenTest, UnflattenCatchesBufferTooSmall) { - char buf[4] = { 0xee, 0xee, 0xee, 0xee }; + unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee }; mBC->set("abcd", 4, "efgh", 4); size_t size = mBC->getFlattenedSize(); diff --git a/libutils/tests/LruCache_test.cpp b/libutils/tests/LruCache_test.cpp index e573952..bcbea32 100644 --- a/libutils/tests/LruCache_test.cpp +++ b/libutils/tests/LruCache_test.cpp @@ -184,7 +184,7 @@ TEST_F(LruCacheTest, StressTest) { for (size_t i = 0; i < kNumKeys; i++) { strings[i] = (char *)malloc(16); - sprintf(strings[i], "%d", i); + sprintf(strings[i], "%zu", i); } srandom(12345); diff --git a/libziparchive/Android.mk b/libziparchive/Android.mk index e754c3b..705caa5 100644 --- a/libziparchive/Android.mk +++ b/libziparchive/Android.mk @@ -30,6 +30,7 @@ LOCAL_SHARED_LIBRARIES := libutils LOCAL_MODULE:= libziparchive LOCAL_C_INCLUDES += ${includes} +LOCAL_CFLAGS := -Werror include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) @@ -40,6 +41,8 @@ LOCAL_C_INCLUDES += ${includes} LOCAL_STATIC_LIBRARIES := libz libutils LOCAL_MODULE:= libziparchive-host +LOCAL_CFLAGS := -Werror +LOCAL_MULTILIB := both include $(BUILD_HOST_STATIC_LIBRARY) include $(CLEAR_VARS) @@ -47,7 +50,8 @@ LOCAL_MODULE := ziparchive-tests LOCAL_CPP_EXTENSION := .cc LOCAL_CFLAGS += \ -DGTEST_OS_LINUX_ANDROID \ - -DGTEST_HAS_STD_STRING + -DGTEST_HAS_STD_STRING \ + -Werror LOCAL_SRC_FILES := zip_archive_test.cc LOCAL_SHARED_LIBRARIES := liblog LOCAL_STATIC_LIBRARIES := libziparchive libz libgtest libgtest_main libutils @@ -58,7 +62,8 @@ LOCAL_MODULE := ziparchive-tests-host LOCAL_CPP_EXTENSION := .cc LOCAL_CFLAGS += \ -DGTEST_OS_LINUX \ - -DGTEST_HAS_STD_STRING + -DGTEST_HAS_STD_STRING \ + -Werror LOCAL_SRC_FILES := zip_archive_test.cc LOCAL_STATIC_LIBRARIES := libziparchive-host \ libz \ diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc index a23d4ae..128bad4 100644 --- a/libziparchive/zip_archive.cc +++ b/libziparchive/zip_archive.cc @@ -17,73 +17,191 @@ /* * Read-only access to Zip archives, with minimal heap allocation. */ -#include "ziparchive/zip_archive.h" - -#include <zlib.h> #include <assert.h> #include <errno.h> +#include <fcntl.h> +#include <inttypes.h> #include <limits.h> #include <log/log.h> -#include <fcntl.h> #include <stdlib.h> #include <string.h> #include <unistd.h> +#include <utils/Compat.h> #include <utils/FileMap.h> +#include <zlib.h> #include <JNIHelp.h> // TEMP_FAILURE_RETRY may or may not be in unistd -// This is for windows. If we don't open a file in binary mode, weirds +#include "ziparchive/zip_archive.h" + +// This is for windows. If we don't open a file in binary mode, weird // things will happen. #ifndef O_BINARY #define O_BINARY 0 #endif -/* - * Zip file constants. - */ -static const uint32_t kEOCDSignature = 0x06054b50; -static const uint32_t kEOCDLen = 2; -static const uint32_t kEOCDNumEntries = 8; // offset to #of entries in file -static const uint32_t kEOCDSize = 12; // size of the central directory -static const uint32_t kEOCDFileOffset = 16; // offset to central directory - -static const uint32_t kMaxCommentLen = 65535; // longest possible in ushort -static const uint32_t kMaxEOCDSearch = (kMaxCommentLen + kEOCDLen); - -static const uint32_t kLFHSignature = 0x04034b50; -static const uint32_t kLFHLen = 30; // excluding variable-len fields -static const uint32_t kLFHGPBFlags = 6; // general purpose bit flags -static const uint32_t kLFHCRC = 14; // offset to CRC -static const uint32_t kLFHCompLen = 18; // offset to compressed length -static const uint32_t kLFHUncompLen = 22; // offset to uncompressed length -static const uint32_t kLFHNameLen = 26; // offset to filename length -static const uint32_t kLFHExtraLen = 28; // offset to extra length - -static const uint32_t kCDESignature = 0x02014b50; -static const uint32_t kCDELen = 46; // excluding variable-len fields -static const uint32_t kCDEMethod = 10; // offset to compression method -static const uint32_t kCDEModWhen = 12; // offset to modification timestamp -static const uint32_t kCDECRC = 16; // offset to entry CRC -static const uint32_t kCDECompLen = 20; // offset to compressed length -static const uint32_t kCDEUncompLen = 24; // offset to uncompressed length -static const uint32_t kCDENameLen = 28; // offset to filename length -static const uint32_t kCDEExtraLen = 30; // offset to extra length -static const uint32_t kCDECommentLen = 32; // offset to comment length -static const uint32_t kCDELocalOffset = 42; // offset to local hdr - -static const uint32_t kDDOptSignature = 0x08074b50; // *OPTIONAL* data descriptor signature -static const uint32_t kDDSignatureLen = 4; -static const uint32_t kDDLen = 12; -static const uint32_t kDDMaxLen = 16; // max of 16 bytes with a signature, 12 bytes without -static const uint32_t kDDCrc32 = 0; // offset to crc32 -static const uint32_t kDDCompLen = 4; // offset to compressed length -static const uint32_t kDDUncompLen = 8; // offset to uncompressed length - -static const uint32_t kGPBDDFlagMask = 0x0008; // mask value that signifies that the entry has a DD - +#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \ + TypeName(); \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) + +// The "end of central directory" (EOCD) record. Each archive +// contains exactly once such record which appears at the end of +// the archive. It contains archive wide information like the +// number of entries in the archive and the offset to the central +// directory of the offset. +struct EocdRecord { + static const uint32_t kSignature = 0x06054b50; + + // End of central directory signature, should always be + // |kSignature|. + uint32_t eocd_signature; + // The number of the current "disk", i.e, the "disk" that this + // central directory is on. + // + // This implementation assumes that each archive spans a single + // disk only. i.e, that disk_num == 1. + uint16_t disk_num; + // The disk where the central directory starts. + // + // This implementation assumes that each archive spans a single + // disk only. i.e, that cd_start_disk == 1. + uint16_t cd_start_disk; + // The number of central directory records on this disk. + // + // This implementation assumes that each archive spans a single + // disk only. i.e, that num_records_on_disk == num_records. + uint16_t num_records_on_disk; + // The total number of central directory records. + uint16_t num_records; + // The size of the central directory (in bytes). + uint32_t cd_size; + // The offset of the start of the central directory, relative + // to the start of the file. + uint32_t cd_start_offset; + // Length of the central directory comment. + uint16_t comment_length; + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(EocdRecord); +} __attribute__((packed)); + +// A structure representing the fixed length fields for a single +// record in the central directory of the archive. In addition to +// the fixed length fields listed here, each central directory +// record contains a variable length "file_name" and "extra_field" +// whose lengths are given by |file_name_length| and |extra_field_length| +// respectively. +struct CentralDirectoryRecord { + static const uint32_t kSignature = 0x02014b50; + + // The start of record signature. Must be |kSignature|. + uint32_t record_signature; + // Tool version. Ignored by this implementation. + uint16_t version_made_by; + // Tool version. Ignored by this implementation. + uint16_t version_needed; + // The "general purpose bit flags" for this entry. The only + // flag value that we currently check for is the "data descriptor" + // flag. + uint16_t gpb_flags; + // The compression method for this entry, one of |kCompressStored| + // and |kCompressDeflated|. + uint16_t compression_method; + // The file modification time and date for this entry. + uint16_t last_mod_time; + uint16_t last_mod_date; + // The CRC-32 checksum for this entry. + uint32_t crc32; + // The compressed size (in bytes) of this entry. + uint32_t compressed_size; + // The uncompressed size (in bytes) of this entry. + uint32_t uncompressed_size; + // The length of the entry file name in bytes. The file name + // will appear immediately after this record. + uint16_t file_name_length; + // The length of the extra field info (in bytes). This data + // will appear immediately after the entry file name. + uint16_t extra_field_length; + // The length of the entry comment (in bytes). This data will + // appear immediately after the extra field. + uint16_t comment_length; + // The start disk for this entry. Ignored by this implementation). + uint16_t file_start_disk; + // File attributes. Ignored by this implementation. + uint16_t internal_file_attributes; + // File attributes. Ignored by this implementation. + uint32_t external_file_attributes; + // The offset to the local file header for this entry, from the + // beginning of this archive. + uint32_t local_file_header_offset; + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(CentralDirectoryRecord); +} __attribute__((packed)); + +// The local file header for a given entry. This duplicates information +// present in the central directory of the archive. It is an error for +// the information here to be different from the central directory +// information for a given entry. +struct LocalFileHeader { + static const uint32_t kSignature = 0x04034b50; + + // The local file header signature, must be |kSignature|. + uint32_t lfh_signature; + // Tool version. Ignored by this implementation. + uint16_t version_needed; + // The "general purpose bit flags" for this entry. The only + // flag value that we currently check for is the "data descriptor" + // flag. + uint16_t gpb_flags; + // The compression method for this entry, one of |kCompressStored| + // and |kCompressDeflated|. + uint16_t compression_method; + // The file modification time and date for this entry. + uint16_t last_mod_time; + uint16_t last_mod_date; + // The CRC-32 checksum for this entry. + uint32_t crc32; + // The compressed size (in bytes) of this entry. + uint32_t compressed_size; + // The uncompressed size (in bytes) of this entry. + uint32_t uncompressed_size; + // The length of the entry file name in bytes. The file name + // will appear immediately after this record. + uint16_t file_name_length; + // The length of the extra field info (in bytes). This data + // will appear immediately after the entry file name. + uint16_t extra_field_length; + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(LocalFileHeader); +} __attribute__((packed)); + +struct DataDescriptor { + // The *optional* data descriptor start signature. + static const uint32_t kOptSignature = 0x08074b50; + + // CRC-32 checksum of the entry. + uint32_t crc32; + // Compressed size of the entry. + uint32_t compressed_size; + // Uncompressed size of the entry. + uint32_t uncompressed_size; + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(DataDescriptor); +} __attribute__((packed)); + +#undef DISALLOW_IMPLICIT_CONSTRUCTORS + +static const uint32_t kGPBDDFlagMask = 0x0008; // mask value that signifies that the entry has a DD static const uint32_t kMaxErrorLen = 1024; +// The maximum size of a central directory or a file +// comment in bytes. +static const uint32_t kMaxCommentLen = 65535; + +// The maximum number of bytes to scan backwards for the EOCD start. +static const uint32_t kMaxEOCDSearch = kMaxCommentLen + sizeof(EocdRecord); + static const char* kErrorMessages[] = { "Unknown return code.", "Iteration ended", @@ -217,8 +335,7 @@ static int32_t CopyFileToFile(int fd, uint8_t* begin, const uint32_t length, uin ssize_t actual = TEMP_FAILURE_RETRY(read(fd, buf, get_size)); if (actual != get_size) { - ALOGW("CopyFileToFile: copy read failed (%d vs %zd)", - (int) actual, get_size); + ALOGW("CopyFileToFile: copy read failed (" ZD " vs " ZD ")", actual, get_size); return kIoError; } @@ -279,7 +396,7 @@ static int64_t EntryToIndex(const ZipEntryName* hash_table, ent = (ent + 1) & (hash_table_size - 1); } - ALOGV("Zip: Unable to find entry %.*s", name_length, name); + ALOGV("Zip: Unable to find entry %.*s", length, name); return kEntryNotFound; } @@ -310,39 +427,21 @@ static int32_t AddToHash(ZipEntryName *hash_table, const uint64_t hash_table_siz return 0; } -/* - * Get 2 little-endian bytes. - */ -static uint16_t get2LE(const uint8_t* src) { - return src[0] | (src[1] << 8); -} - -/* - * Get 4 little-endian bytes. - */ -static uint32_t get4LE(const uint8_t* src) { - uint32_t result; - - result = src[0]; - result |= src[1] << 8; - result |= src[2] << 16; - result |= src[3] << 24; - - return result; -} - static int32_t MapCentralDirectory0(int fd, const char* debug_file_name, ZipArchive* archive, off64_t file_length, - uint32_t read_amount, uint8_t* scan_buffer) { + off64_t read_amount, uint8_t* scan_buffer) { const off64_t search_start = file_length - read_amount; if (lseek64(fd, search_start, SEEK_SET) != search_start) { - ALOGW("Zip: seek %lld failed: %s", search_start, strerror(errno)); + ALOGW("Zip: seek %" PRId64 " failed: %s", static_cast<int64_t>(search_start), + strerror(errno)); return kIoError; } - ssize_t actual = TEMP_FAILURE_RETRY(read(fd, scan_buffer, read_amount)); - if (actual != (ssize_t) read_amount) { - ALOGW("Zip: read %zd failed: %s", read_amount, strerror(errno)); + ssize_t actual = TEMP_FAILURE_RETRY( + read(fd, scan_buffer, static_cast<size_t>(read_amount))); + if (actual != static_cast<ssize_t>(read_amount)) { + ALOGW("Zip: read %" PRId64 " failed: %s", static_cast<int64_t>(read_amount), + strerror(errno)); return kIoError; } @@ -352,9 +451,10 @@ static int32_t MapCentralDirectory0(int fd, const char* debug_file_name, * doing an initial minimal read; if we don't find it, retry with a * second read as above.) */ - int i; - for (i = read_amount - kEOCDLen; i >= 0; i--) { - if (scan_buffer[i] == 0x50 && get4LE(&scan_buffer[i]) == kEOCDSignature) { + int i = read_amount - sizeof(EocdRecord); + for (; i >= 0; i--) { + if (scan_buffer[i] == 0x50 && + ((*reinterpret_cast<uint32_t*>(&scan_buffer[i])) == EocdRecord::kSignature)) { ALOGV("+++ Found EOCD at buf+%d", i); break; } @@ -365,46 +465,52 @@ static int32_t MapCentralDirectory0(int fd, const char* debug_file_name, } const off64_t eocd_offset = search_start + i; - const uint8_t* eocd_ptr = scan_buffer + i; - - assert(eocd_offset < file_length); + const EocdRecord* eocd = reinterpret_cast<const EocdRecord*>(scan_buffer + i); + /* + * Verify that there's no trailing space at the end of the central directory + * and its comment. + */ + const off64_t calculated_length = eocd_offset + sizeof(EocdRecord) + + eocd->comment_length; + if (calculated_length != file_length) { + ALOGW("Zip: %" PRId64 " extraneous bytes at the end of the central directory", + static_cast<int64_t>(file_length - calculated_length)); + return kInvalidFile; + } /* * Grab the CD offset and size, and the number of entries in the - * archive. Verify that they look reasonable. Widen dir_size and - * dir_offset to the file offset type. + * archive and verify that they look reasonable. */ - const uint16_t num_entries = get2LE(eocd_ptr + kEOCDNumEntries); - const off64_t dir_size = get4LE(eocd_ptr + kEOCDSize); - const off64_t dir_offset = get4LE(eocd_ptr + kEOCDFileOffset); - - if (dir_offset + dir_size > eocd_offset) { - ALOGW("Zip: bad offsets (dir %lld, size %lld, eocd %lld)", - dir_offset, dir_size, eocd_offset); + if (eocd->cd_start_offset + eocd->cd_size > eocd_offset) { + ALOGW("Zip: bad offsets (dir %" PRIu32 ", size %" PRIu32 ", eocd %" PRId64 ")", + eocd->cd_start_offset, eocd->cd_size, static_cast<int64_t>(eocd_offset)); return kInvalidOffset; } - if (num_entries == 0) { + if (eocd->num_records == 0) { ALOGW("Zip: empty archive?"); return kEmptyArchive; } - ALOGV("+++ num_entries=%d dir_size=%d dir_offset=%d", num_entries, dir_size, - dir_offset); + ALOGV("+++ num_entries=%" PRIu32 "dir_size=%" PRIu32 " dir_offset=%" PRIu32, + eocd->num_records, eocd->cd_size, eocd->cd_start_offset); /* * It all looks good. Create a mapping for the CD, and set the fields * in archive. */ - android::FileMap* map = MapFileSegment(fd, dir_offset, dir_size, - true /* read only */, debug_file_name); + android::FileMap* map = MapFileSegment(fd, + static_cast<off64_t>(eocd->cd_start_offset), + static_cast<size_t>(eocd->cd_size), + true /* read only */, debug_file_name); if (map == NULL) { archive->directory_map = NULL; return kMmapFailed; } archive->directory_map = map; - archive->num_entries = num_entries; - archive->directory_offset = dir_offset; + archive->num_entries = eocd->num_records; + archive->directory_offset = eocd->cd_start_offset; return 0; } @@ -430,12 +536,12 @@ static int32_t MapCentralDirectory(int fd, const char* debug_file_name, } if (file_length > (off64_t) 0xffffffff) { - ALOGV("Zip: zip file too long %d", file_length); + ALOGV("Zip: zip file too long %" PRId64, static_cast<int64_t>(file_length)); return kInvalidFile; } - if (file_length < (int64_t) kEOCDLen) { - ALOGV("Zip: length %ld is too small to be zip", file_length); + if (file_length < static_cast<off64_t>(sizeof(EocdRecord))) { + ALOGV("Zip: length %" PRId64 " is too small to be zip", static_cast<int64_t>(file_length)); return kInvalidFile; } @@ -451,12 +557,12 @@ static int32_t MapCentralDirectory(int fd, const char* debug_file_name, * * We start by pulling in the last part of the file. */ - uint32_t read_amount = kMaxEOCDSearch; - if (file_length < (off64_t) read_amount) { + off64_t read_amount = kMaxEOCDSearch; + if (file_length < read_amount) { read_amount = file_length; } - uint8_t* scan_buffer = (uint8_t*) malloc(read_amount); + uint8_t* scan_buffer = reinterpret_cast<uint8_t*>(malloc(read_amount)); int32_t result = MapCentralDirectory0(fd, debug_file_name, archive, file_length, read_amount, scan_buffer); @@ -472,9 +578,9 @@ static int32_t MapCentralDirectory(int fd, const char* debug_file_name, */ static int32_t ParseZipArchive(ZipArchive* archive) { int32_t result = -1; - const uint8_t* cd_ptr = (const uint8_t*) archive->directory_map->getDataPtr(); - size_t cd_length = archive->directory_map->getDataLength(); - uint16_t num_entries = archive->num_entries; + const uint8_t* const cd_ptr = (const uint8_t*) archive->directory_map->getDataPtr(); + const size_t cd_length = archive->directory_map->getDataLength(); + const uint16_t num_entries = archive->num_entries; /* * Create hash table. We have a minimum 75% load factor, possibly as @@ -489,45 +595,49 @@ static int32_t ParseZipArchive(ZipArchive* archive) { * Walk through the central directory, adding entries to the hash * table and verifying values. */ + const uint8_t* const cd_end = cd_ptr + cd_length; const uint8_t* ptr = cd_ptr; for (uint16_t i = 0; i < num_entries; i++) { - if (get4LE(ptr) != kCDESignature) { - ALOGW("Zip: missed a central dir sig (at %d)", i); + const CentralDirectoryRecord* cdr = + reinterpret_cast<const CentralDirectoryRecord*>(ptr); + if (cdr->record_signature != CentralDirectoryRecord::kSignature) { + ALOGW("Zip: missed a central dir sig (at %" PRIu16 ")", i); goto bail; } - if (ptr + kCDELen > cd_ptr + cd_length) { - ALOGW("Zip: ran off the end (at %d)", i); + if (ptr + sizeof(CentralDirectoryRecord) > cd_end) { + ALOGW("Zip: ran off the end (at %" PRIu16 ")", i); goto bail; } - const off64_t local_header_offset = get4LE(ptr + kCDELocalOffset); + const off64_t local_header_offset = cdr->local_file_header_offset; if (local_header_offset >= archive->directory_offset) { - ALOGW("Zip: bad LFH offset %lld at entry %d", local_header_offset, i); + ALOGW("Zip: bad LFH offset %" PRId64 " at entry %" PRIu16, (int64_t)local_header_offset, i); goto bail; } - const uint16_t file_name_length = get2LE(ptr + kCDENameLen); - const uint16_t extra_length = get2LE(ptr + kCDEExtraLen); - const uint16_t comment_length = get2LE(ptr + kCDECommentLen); + const uint16_t file_name_length = cdr->file_name_length; + const uint16_t extra_length = cdr->extra_field_length; + const uint16_t comment_length = cdr->comment_length; /* add the CDE filename to the hash table */ + const char* file_name = reinterpret_cast<const char *>(ptr + sizeof(CentralDirectoryRecord)); const int add_result = AddToHash(archive->hash_table, - archive->hash_table_size, (const char*) ptr + kCDELen, file_name_length); + archive->hash_table_size, file_name, file_name_length); if (add_result) { ALOGW("Zip: Error adding entry to hash table %d", add_result); result = add_result; goto bail; } - ptr += kCDELen + file_name_length + extra_length + comment_length; - if ((size_t)(ptr - cd_ptr) > cd_length) { - ALOGW("Zip: bad CD advance (%d vs %zd) at entry %d", - (int) (ptr - cd_ptr), cd_length, i); + ptr += sizeof(CentralDirectoryRecord) + file_name_length + extra_length + comment_length; + if ((ptr - cd_ptr) > static_cast<int64_t>(cd_length)) { + ALOGW("Zip: bad CD advance (%tu vs %zu) at entry %" PRIu16, + ptr - cd_ptr, cd_length, i); goto bail; } } - ALOGV("+++ zip good scan %d entries", num_entries); + ALOGV("+++ zip good scan %" PRIu16 " entries", num_entries); result = 0; @@ -591,32 +701,24 @@ void CloseArchive(ZipArchiveHandle handle) { archive->directory_map->release(); } free(archive->hash_table); - - /* ensure nobody tries to use the ZipArchive after it's closed */ - archive->directory_offset = -1; - archive->fd = -1; - archive->num_entries = -1; - archive->hash_table_size = -1; - archive->hash_table = NULL; + free(archive); } static int32_t UpdateEntryFromDataDescriptor(int fd, ZipEntry *entry) { - uint8_t ddBuf[kDDMaxLen]; + uint8_t ddBuf[sizeof(DataDescriptor) + sizeof(DataDescriptor::kOptSignature)]; ssize_t actual = TEMP_FAILURE_RETRY(read(fd, ddBuf, sizeof(ddBuf))); if (actual != sizeof(ddBuf)) { return kIoError; } - const uint32_t ddSignature = get4LE(ddBuf); - uint16_t ddOffset = 0; - if (ddSignature == kDDOptSignature) { - ddOffset = 4; - } + const uint32_t ddSignature = *(reinterpret_cast<const uint32_t*>(ddBuf)); + const uint16_t offset = (ddSignature == DataDescriptor::kOptSignature) ? 4 : 0; + const DataDescriptor* descriptor = reinterpret_cast<const DataDescriptor*>(ddBuf + offset); - entry->crc32 = get4LE(ddBuf + ddOffset + kDDCrc32); - entry->compressed_length = get4LE(ddBuf + ddOffset + kDDCompLen); - entry->uncompressed_length = get4LE(ddBuf + ddOffset + kDDUncompLen); + entry->crc32 = descriptor->crc32; + entry->compressed_length = descriptor->compressed_size; + entry->uncompressed_length = descriptor->uncompressed_size; return 0; } @@ -636,7 +738,7 @@ static inline ssize_t ReadAtOffset(int fd, uint8_t* buf, size_t len, // is Windows. Only recent versions of windows support unix like forks, // and even there the semantics are quite different. if (lseek64(fd, off, SEEK_SET) != off) { - ALOGW("Zip: failed seek to offset %lld", off); + ALOGW("Zip: failed seek to offset %" PRId64, off); return kIoError; } @@ -652,19 +754,22 @@ static int32_t FindEntry(const ZipArchive* archive, const int ent, // Recover the start of the central directory entry from the filename // pointer. The filename is the first entry past the fixed-size data, // so we can just subtract back from that. - const unsigned char* ptr = (const unsigned char*) name; - ptr -= kCDELen; + const uint8_t* ptr = reinterpret_cast<const uint8_t*>(name); + ptr -= sizeof(CentralDirectoryRecord); // This is the base of our mmapped region, we have to sanity check that // the name that's in the hash table is a pointer to a location within // this mapped region. - const unsigned char* base_ptr = (const unsigned char*) - archive->directory_map->getDataPtr(); + const uint8_t* base_ptr = reinterpret_cast<const uint8_t*>( + archive->directory_map->getDataPtr()); if (ptr < base_ptr || ptr > base_ptr + archive->directory_map->getDataLength()) { ALOGW("Zip: Invalid entry pointer"); return kInvalidOffset; } + const CentralDirectoryRecord *cdr = + reinterpret_cast<const CentralDirectoryRecord*>(ptr); + // The offset of the start of the central directory in the zipfile. // We keep this lying around so that we can sanity check all our lengths // and our per-file structures. @@ -673,52 +778,48 @@ static int32_t FindEntry(const ZipArchive* archive, const int ent, // Fill out the compression method, modification time, crc32 // and other interesting attributes from the central directory. These // will later be compared against values from the local file header. - data->method = get2LE(ptr + kCDEMethod); - data->mod_time = get4LE(ptr + kCDEModWhen); - data->crc32 = get4LE(ptr + kCDECRC); - data->compressed_length = get4LE(ptr + kCDECompLen); - data->uncompressed_length = get4LE(ptr + kCDEUncompLen); + data->method = cdr->compression_method; + data->mod_time = cdr->last_mod_time; + data->crc32 = cdr->crc32; + data->compressed_length = cdr->compressed_size; + data->uncompressed_length = cdr->uncompressed_size; // Figure out the local header offset from the central directory. The // actual file data will begin after the local header and the name / // extra comments. - const off64_t local_header_offset = get4LE(ptr + kCDELocalOffset); - if (local_header_offset + (off64_t) kLFHLen >= cd_offset) { + const off64_t local_header_offset = cdr->local_file_header_offset; + if (local_header_offset + static_cast<off64_t>(sizeof(LocalFileHeader)) >= cd_offset) { ALOGW("Zip: bad local hdr offset in zip"); return kInvalidOffset; } - uint8_t lfh_buf[kLFHLen]; + uint8_t lfh_buf[sizeof(LocalFileHeader)]; ssize_t actual = ReadAtOffset(archive->fd, lfh_buf, sizeof(lfh_buf), local_header_offset); if (actual != sizeof(lfh_buf)) { - ALOGW("Zip: failed reading lfh name from offset %lld", local_header_offset); + ALOGW("Zip: failed reading lfh name from offset %" PRId64, (int64_t)local_header_offset); return kIoError; } - if (get4LE(lfh_buf) != kLFHSignature) { - ALOGW("Zip: didn't find signature at start of lfh, offset=%lld", - local_header_offset); + const LocalFileHeader *lfh = reinterpret_cast<const LocalFileHeader*>(lfh_buf); + + if (lfh->lfh_signature != LocalFileHeader::kSignature) { + ALOGW("Zip: didn't find signature at start of lfh, offset=%" PRId64, + static_cast<int64_t>(local_header_offset)); return kInvalidOffset; } // Paranoia: Match the values specified in the local file header // to those specified in the central directory. - const uint16_t lfhGpbFlags = get2LE(lfh_buf + kLFHGPBFlags); - const uint16_t lfhNameLen = get2LE(lfh_buf + kLFHNameLen); - const uint16_t lfhExtraLen = get2LE(lfh_buf + kLFHExtraLen); - - if ((lfhGpbFlags & kGPBDDFlagMask) == 0) { - const uint32_t lfhCrc = get4LE(lfh_buf + kLFHCRC); - const uint32_t lfhCompLen = get4LE(lfh_buf + kLFHCompLen); - const uint32_t lfhUncompLen = get4LE(lfh_buf + kLFHUncompLen); - + if ((lfh->gpb_flags & kGPBDDFlagMask) == 0) { data->has_data_descriptor = 0; - if (data->compressed_length != lfhCompLen || data->uncompressed_length != lfhUncompLen - || data->crc32 != lfhCrc) { - ALOGW("Zip: size/crc32 mismatch. expected {%d, %d, %x}, was {%d, %d, %x}", + if (data->compressed_length != lfh->compressed_size + || data->uncompressed_length != lfh->uncompressed_size + || data->crc32 != lfh->crc32) { + ALOGW("Zip: size/crc32 mismatch. expected {%" PRIu32 ", %" PRIu32 + ", %" PRIx32 "}, was {%" PRIu32 ", %" PRIu32 ", %" PRIx32 "}", data->compressed_length, data->uncompressed_length, data->crc32, - lfhCompLen, lfhUncompLen, lfhCrc); + lfh->compressed_size, lfh->uncompressed_size, lfh->crc32); return kInconsistentInformation; } } else { @@ -727,9 +828,9 @@ static int32_t FindEntry(const ZipArchive* archive, const int ent, // Check that the local file header name matches the declared // name in the central directory. - if (lfhNameLen == nameLen) { - const off64_t name_offset = local_header_offset + kLFHLen; - if (name_offset + lfhNameLen >= cd_offset) { + if (lfh->file_name_length == nameLen) { + const off64_t name_offset = local_header_offset + sizeof(LocalFileHeader); + if (name_offset + lfh->file_name_length >= cd_offset) { ALOGW("Zip: Invalid declared length"); return kInvalidOffset; } @@ -739,7 +840,7 @@ static int32_t FindEntry(const ZipArchive* archive, const int ent, name_offset); if (actual != nameLen) { - ALOGW("Zip: failed reading lfh name from offset %lld", name_offset); + ALOGW("Zip: failed reading lfh name from offset %" PRId64, (int64_t)name_offset); free(name_buf); return kIoError; } @@ -755,22 +856,23 @@ static int32_t FindEntry(const ZipArchive* archive, const int ent, return kInconsistentInformation; } - const off64_t data_offset = local_header_offset + kLFHLen + lfhNameLen + lfhExtraLen; + const off64_t data_offset = local_header_offset + sizeof(LocalFileHeader) + + lfh->file_name_length + lfh->extra_field_length; if (data_offset > cd_offset) { - ALOGW("Zip: bad data offset %lld in zip", (off64_t) data_offset); + ALOGW("Zip: bad data offset %" PRId64 " in zip", (int64_t)data_offset); return kInvalidOffset; } if ((off64_t)(data_offset + data->compressed_length) > cd_offset) { - ALOGW("Zip: bad compressed length in zip (%lld + %zd > %lld)", - data_offset, data->compressed_length, cd_offset); + ALOGW("Zip: bad compressed length in zip (%" PRId64 " + %" PRIu32 " > %" PRId64 ")", + (int64_t)data_offset, data->compressed_length, (int64_t)cd_offset); return kInvalidOffset; } if (data->method == kCompressStored && (off64_t)(data_offset + data->uncompressed_length) > cd_offset) { - ALOGW("Zip: bad uncompressed length in zip (%lld + %zd > %lld)", - data_offset, data->uncompressed_length, cd_offset); + ALOGW("Zip: bad uncompressed length in zip (%" PRId64 " + %" PRIu32 " > %" PRId64 ")", + (int64_t)data_offset, data->uncompressed_length, (int64_t)cd_offset); return kInvalidOffset; } @@ -906,10 +1008,10 @@ static int32_t InflateToFile(int fd, const ZipEntry* entry, do { /* read as much as we can */ if (zstream.avail_in == 0) { - const ssize_t getSize = (compressed_length > kBufSize) ? kBufSize : compressed_length; - const ssize_t actual = TEMP_FAILURE_RETRY(read(fd, read_buf, getSize)); + const ZD_TYPE getSize = (compressed_length > kBufSize) ? kBufSize : compressed_length; + const ZD_TYPE actual = TEMP_FAILURE_RETRY(read(fd, read_buf, getSize)); if (actual != getSize) { - ALOGW("Zip: inflate read failed (%d vs %zd)", actual, getSize); + ALOGW("Zip: inflate read failed (" ZD " vs " ZD ")", actual, getSize); result = kIoError; goto z_bail; } @@ -952,7 +1054,7 @@ static int32_t InflateToFile(int fd, const ZipEntry* entry, *crc_out = zstream.adler; if (zstream.total_out != uncompressed_length || compressed_length != 0) { - ALOGW("Zip: size mismatch on inflated file (%ld vs %zd)", + ALOGW("Zip: size mismatch on inflated file (%lu vs %" PRIu32 ")", zstream.total_out, uncompressed_length); result = kInconsistentInformation; goto z_bail; @@ -973,7 +1075,7 @@ int32_t ExtractToMemory(ZipArchiveHandle handle, off64_t data_offset = entry->offset; if (lseek64(archive->fd, data_offset, SEEK_SET) != data_offset) { - ALOGW("Zip: lseek to data at %lld failed", (off64_t) data_offset); + ALOGW("Zip: lseek to data at %" PRId64 " failed", (int64_t)data_offset); return kIoError; } @@ -996,7 +1098,7 @@ int32_t ExtractToMemory(ZipArchiveHandle handle, // TODO: Fix this check by passing the right flags to inflate2 so that // it calculates the CRC for us. if (entry->crc32 != crc && false) { - ALOGW("Zip: crc mismatch: expected %u, was %llu", entry->crc32, crc); + ALOGW("Zip: crc mismatch: expected %" PRIu32 ", was %" PRIu64, entry->crc32, crc); return kInconsistentInformation; } @@ -1016,8 +1118,8 @@ int32_t ExtractEntryToFile(ZipArchiveHandle handle, int result = TEMP_FAILURE_RETRY(ftruncate(fd, declared_length + current_offset)); if (result == -1) { - ALOGW("Zip: unable to truncate file to %lld: %s", declared_length + current_offset, - strerror(errno)); + ALOGW("Zip: unable to truncate file to %" PRId64 ": %s", + (int64_t)(declared_length + current_offset), strerror(errno)); return kIoError; } diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc index 3082216..875b6de 100644 --- a/libziparchive/zip_archive_test.cc +++ b/libziparchive/zip_archive_test.cc @@ -140,11 +140,7 @@ TEST(ziparchive, ExtractToMemory) { CloseArchive(handle); } -TEST(ziparchive, EmptyEntries) { - char temp_file_pattern[] = "empty_entries_test_XXXXXX"; - int fd = mkstemp(temp_file_pattern); - ASSERT_NE(-1, fd); - const uint32_t data[] = { +static const uint32_t kEmptyEntriesZip[] = { 0x04034b50, 0x0000000a, 0x63600000, 0x00004438, 0x00000000, 0x00000000, 0x00090000, 0x6d65001c, 0x2e797470, 0x55747874, 0x03000954, 0x52e25c13, 0x52e25c24, 0x000b7875, 0x42890401, 0x88040000, 0x50000013, 0x1e02014b, @@ -152,8 +148,28 @@ TEST(ziparchive, EmptyEntries) { 0x00001800, 0x00000000, 0xa0000000, 0x00000081, 0x706d6500, 0x742e7974, 0x54557478, 0x13030005, 0x7552e25c, 0x01000b78, 0x00428904, 0x13880400, 0x4b500000, 0x00000605, 0x00010000, 0x004f0001, 0x00430000, 0x00000000 }; - const ssize_t file_size = 168; - ASSERT_EQ(file_size, TEMP_FAILURE_RETRY(write(fd, data, file_size))); + +static int make_temporary_file(const char* file_name_pattern) { + char full_path[1024]; + // Account for differences between the host and the target. + // + // TODO: Maybe reuse bionic/tests/TemporaryFile.h. + snprintf(full_path, sizeof(full_path), "/data/local/tmp/%s", file_name_pattern); + int fd = mkstemp(full_path); + if (fd == -1) { + snprintf(full_path, sizeof(full_path), "/tmp/%s", file_name_pattern); + fd = mkstemp(full_path); + } + + return fd; +} + +TEST(ziparchive, EmptyEntries) { + char temp_file_pattern[] = "empty_entries_test_XXXXXX"; + int fd = make_temporary_file(temp_file_pattern); + ASSERT_NE(-1, fd); + const ssize_t file_size = sizeof(kEmptyEntriesZip); + ASSERT_EQ(file_size, TEMP_FAILURE_RETRY(write(fd, kEmptyEntriesZip, file_size))); ZipArchiveHandle handle; ASSERT_EQ(0, OpenArchiveFd(fd, "EmptyEntriesTest", &handle)); @@ -165,7 +181,7 @@ TEST(ziparchive, EmptyEntries) { ASSERT_EQ(0, ExtractToMemory(handle, &entry, buffer, 1)); char output_file_pattern[] = "empty_entries_output_XXXXXX"; - int output_fd = mkstemp(output_file_pattern); + int output_fd = make_temporary_file(output_file_pattern); ASSERT_NE(-1, output_fd); ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, output_fd)); @@ -177,9 +193,25 @@ TEST(ziparchive, EmptyEntries) { close(output_fd); } +TEST(ziparchive, TrailerAfterEOCD) { + char temp_file_pattern[] = "trailer_after_eocd_test_XXXXXX"; + int fd = make_temporary_file(temp_file_pattern); + ASSERT_NE(-1, fd); + + // Create a file with 8 bytes of random garbage. + static const uint8_t trailer[] = { 'A' ,'n', 'd', 'r', 'o', 'i', 'd', 'z' }; + const ssize_t file_size = sizeof(kEmptyEntriesZip); + const ssize_t trailer_size = sizeof(trailer); + ASSERT_EQ(file_size, TEMP_FAILURE_RETRY(write(fd, kEmptyEntriesZip, file_size))); + ASSERT_EQ(trailer_size, TEMP_FAILURE_RETRY(write(fd, trailer, trailer_size))); + + ZipArchiveHandle handle; + ASSERT_GT(0, OpenArchiveFd(fd, "EmptyEntriesTest", &handle)); +} + TEST(ziparchive, ExtractToFile) { char kTempFilePattern[] = "zip_archive_input_XXXXXX"; - int fd = mkstemp(kTempFilePattern); + int fd = make_temporary_file(kTempFilePattern); ASSERT_NE(-1, fd); const uint8_t data[8] = { '1', '2', '3', '4', '5', '6', '7', '8' }; const ssize_t data_size = sizeof(data); @@ -209,7 +241,8 @@ TEST(ziparchive, ExtractToFile) { sizeof(kATxtContents))); // Assert that the total length of the file is sane - ASSERT_EQ(data_size + sizeof(kATxtContents), lseek64(fd, 0, SEEK_END)); + ASSERT_EQ(data_size + static_cast<ssize_t>(sizeof(kATxtContents)), + lseek64(fd, 0, SEEK_END)); close(fd); } @@ -247,4 +280,3 @@ int main(int argc, char** argv) { return RUN_ALL_TESTS(); } - diff --git a/libzipfile/Android.mk b/libzipfile/Android.mk index d2d758c..12a2229 100644 --- a/libzipfile/Android.mk +++ b/libzipfile/Android.mk @@ -14,6 +14,10 @@ LOCAL_MODULE:= libzipfile LOCAL_C_INCLUDES += external/zlib +LOCAL_CFLAGS := -Werror + +LOCAL_MULTILIB := both + include $(BUILD_HOST_STATIC_LIBRARY) # build device static library @@ -30,6 +34,8 @@ LOCAL_MODULE:= libzipfile LOCAL_C_INCLUDES += external/zlib +LOCAL_CFLAGS := -Werror + include $(BUILD_STATIC_LIBRARY) @@ -45,4 +51,6 @@ LOCAL_MODULE := test_zipfile LOCAL_C_INCLUDES += external/zlib +LOCAL_CFLAGS := -Werror + include $(BUILD_HOST_EXECUTABLE) diff --git a/libzipfile/centraldir.c b/libzipfile/centraldir.c index 911e2b9..69cf47a 100644 --- a/libzipfile/centraldir.c +++ b/libzipfile/centraldir.c @@ -3,6 +3,8 @@ #include <string.h>
#include <stdlib.h>
+#include <utils/Compat.h>
+
enum {
// finding the directory
CD_SIGNATURE = 0x06054b50,
@@ -66,24 +68,10 @@ read_central_directory_entry(Zipfile* file, Zipentry* entry, {
const unsigned char* p;
- unsigned short versionMadeBy;
- unsigned short versionToExtract;
- unsigned short gpBitFlag;
- unsigned short compressionMethod;
- unsigned short lastModFileTime;
- unsigned short lastModFileDate;
- unsigned long crc32;
unsigned short extraFieldLength;
unsigned short fileCommentLength;
- unsigned short diskNumberStart;
- unsigned short internalAttrs;
- unsigned long externalAttrs;
unsigned long localHeaderRelOffset;
- const unsigned char* extraField;
- const unsigned char* fileComment;
unsigned int dataOffset;
- unsigned short lfhExtraFieldSize;
-
p = *buf;
@@ -97,21 +85,12 @@ read_central_directory_entry(Zipfile* file, Zipentry* entry, return -1;
}
- versionMadeBy = read_le_short(&p[0x04]);
- versionToExtract = read_le_short(&p[0x06]);
- gpBitFlag = read_le_short(&p[0x08]);
entry->compressionMethod = read_le_short(&p[0x0a]);
- lastModFileTime = read_le_short(&p[0x0c]);
- lastModFileDate = read_le_short(&p[0x0e]);
- crc32 = read_le_int(&p[0x10]);
entry->compressedSize = read_le_int(&p[0x14]);
entry->uncompressedSize = read_le_int(&p[0x18]);
entry->fileNameLength = read_le_short(&p[0x1c]);
extraFieldLength = read_le_short(&p[0x1e]);
fileCommentLength = read_le_short(&p[0x20]);
- diskNumberStart = read_le_short(&p[0x22]);
- internalAttrs = read_le_short(&p[0x24]);
- externalAttrs = read_le_int(&p[0x26]);
localHeaderRelOffset = read_le_int(&p[0x2a]);
p += ENTRY_LEN;
@@ -125,19 +104,9 @@ read_central_directory_entry(Zipfile* file, Zipentry* entry, p += entry->fileNameLength;
// extra field
- if (extraFieldLength != 0) {
- extraField = p;
- } else {
- extraField = NULL;
- }
p += extraFieldLength;
// comment, if any
- if (fileCommentLength != 0) {
- fileComment = p;
- } else {
- fileComment = NULL;
- }
p += fileCommentLength;
*buf = p;
@@ -183,7 +152,7 @@ read_central_dir(Zipfile *file) int err;
const unsigned char* buf = file->buf;
- ssize_t bufsize = file->bufsize;
+ ZD_TYPE bufsize = file->bufsize;
const unsigned char* eocd;
const unsigned char* p;
const unsigned char* start;
@@ -192,7 +161,7 @@ read_central_dir(Zipfile *file) // too small to be a ZIP archive?
if (bufsize < EOCD_LEN) {
- fprintf(stderr, "Length is %zd -- too small\n", bufsize);
+ fprintf(stderr, "Length is " ZD " -- too small\n", bufsize);
goto bail;
}
diff --git a/libzipfile/zipfile.c b/libzipfile/zipfile.c index a401a9b..b903fcf 100644 --- a/libzipfile/zipfile.c +++ b/libzipfile/zipfile.c @@ -79,7 +79,6 @@ static int uninflate(unsigned char* out, int unlen, const unsigned char* in, int clen) { z_stream zstream; - unsigned long crc; int err = 0; int zerr; diff --git a/logcat/Android.mk b/logcat/Android.mk index b5e27eb..f46a4de 100644 --- a/logcat/Android.mk +++ b/logcat/Android.mk @@ -7,7 +7,9 @@ LOCAL_SRC_FILES:= logcat.cpp event.logtags LOCAL_SHARED_LIBRARIES := liblog -LOCAL_MODULE:= logcat +LOCAL_MODULE := logcat + +LOCAL_CFLAGS := -Werror include $(BUILD_EXECUTABLE) diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp index 3c33938..7c6af42 100644 --- a/logcat/logcat.cpp +++ b/logcat/logcat.cpp @@ -17,6 +17,7 @@ #include <cutils/sockets.h> #include <log/log.h> +#include <log/log_read.h> #include <log/logger.h> #include <log/logd.h> #include <log/logprint.h> @@ -225,11 +226,21 @@ static void show_help(const char *cmd) " -t <count> print only the most recent <count> lines (implies -d)\n" " -T <count> print only the most recent <count> lines (does not imply -d)\n" " -g get the size of the log's ring buffer and exit\n" - " -b <buffer> Request alternate ring buffer, 'main', 'system', 'radio'\n" - " or 'events'. Multiple -b parameters are allowed and the\n" - " results are interleaved. The default is -b main -b system.\n" - " -B output the log in binary"); - + " -b <buffer> Request alternate ring buffer, 'main', 'system', 'radio',\n" + " 'events', 'crash' or 'all'. Multiple -b parameters are\n" + " allowed and results are interleaved. The default is\n" + " -b main -b system -b crash.\n" + " -B output the log in binary.\n" + " -S output statistics.\n" + " -G <size> set size of log ring buffer, may suffix with K or M.\n" + " -p print prune white and ~black list. Service is specified as\n" + " UID, UID/PID or /PID. Weighed for quicker pruning if prefix\n" + " with ~, otherwise weighed for longevity if unadorned. All\n" + " other pruning activity is oldest first. Special case ~!\n" + " represents an automatic quicker pruning for the noisiest\n" + " UID as determined by the current statistics.\n" + " -P '<list> ...' set prune white and ~black list, using same format as\n" + " printed above. Must be quoted.\n"); fprintf(stderr,"\nfilterspecs are a series of \n" " <tag>[:priority]\n\n" @@ -270,7 +281,29 @@ static int setLogFormat(const char * formatString) return 0; } -extern "C" void logprint_run_tests(void); +static const char multipliers[][2] = { + { "" }, + { "K" }, + { "M" }, + { "G" } +}; + +static unsigned long value_of_size(unsigned long value) +{ + for (unsigned i = 0; + (i < sizeof(multipliers)/sizeof(multipliers[0])) && (value >= 1024); + value /= 1024, ++i) ; + return value; +} + +static const char *multiplier_of_size(unsigned long value) +{ + unsigned i; + for (i = 0; + (i < sizeof(multipliers)/sizeof(multipliers[0])) && (value >= 1024); + value /= 1024, ++i) ; + return multipliers[i]; +} int main(int argc, char **argv) { @@ -278,23 +311,23 @@ int main(int argc, char **argv) int hasSetLogFormat = 0; int clearLog = 0; int getLogSize = 0; + unsigned long setLogSize = 0; + int getPruneList = 0; + char *setPruneList = NULL; + int printStatistics = 0; int mode = O_RDONLY; const char *forceFilters = NULL; log_device_t* devices = NULL; log_device_t* dev; bool needBinary = false; struct logger_list *logger_list; - int tail_lines = 0; + unsigned int tail_lines = 0; + log_time tail_time(log_time::EPOCH); signal(SIGPIPE, exit); g_logformat = android_log_format_new(); - if (argc == 2 && 0 == strcmp(argv[1], "--test")) { - logprint_run_tests(); - exit(0); - } - if (argc == 2 && 0 == strcmp(argv[1], "--help")) { android::show_help(argv[0]); exit(0); @@ -303,7 +336,7 @@ int main(int argc, char **argv) for (;;) { int ret; - ret = getopt(argc, argv, "cdt:T:gsQf:r::n:v:b:B"); + ret = getopt(argc, argv, "cdt:T:gG:sQf:r:n:v:b:BSpP:"); if (ret < 0) { break; @@ -328,14 +361,124 @@ int main(int argc, char **argv) mode = O_RDONLY | O_NDELAY; /* FALLTHRU */ case 'T': - tail_lines = atoi(optarg); + if (strspn(optarg, "0123456789") != strlen(optarg)) { + char *cp = tail_time.strptime(optarg, + log_time::default_format); + if (!cp) { + fprintf(stderr, + "ERROR: -%c \"%s\" not in \"%s\" time format\n", + ret, optarg, log_time::default_format); + exit(1); + } + if (*cp) { + char c = *cp; + *cp = '\0'; + fprintf(stderr, + "WARNING: -%c \"%s\"\"%c%s\" time truncated\n", + ret, optarg, c, cp + 1); + *cp = c; + } + } else { + tail_lines = atoi(optarg); + if (!tail_lines) { + fprintf(stderr, + "WARNING: -%c %s invalid, setting to 1\n", + ret, optarg); + tail_lines = 1; + } + } break; case 'g': getLogSize = 1; break; + case 'G': { + // would use atol if not for the multiplier + char *cp = optarg; + setLogSize = 0; + while (('0' <= *cp) && (*cp <= '9')) { + setLogSize *= 10; + setLogSize += *cp - '0'; + ++cp; + } + + switch(*cp) { + case 'g': + case 'G': + setLogSize *= 1024; + /* FALLTHRU */ + case 'm': + case 'M': + setLogSize *= 1024; + /* FALLTHRU */ + case 'k': + case 'K': + setLogSize *= 1024; + /* FALLTHRU */ + case '\0': + break; + + default: + setLogSize = 0; + } + + if (!setLogSize) { + fprintf(stderr, "ERROR: -G <num><multiplier>\n"); + exit(1); + } + } + break; + + case 'p': + getPruneList = 1; + break; + + case 'P': + setPruneList = optarg; + break; + case 'b': { + if (strcmp(optarg, "all") == 0) { + while (devices) { + dev = devices; + devices = dev->next; + delete dev; + } + + dev = devices = new log_device_t("main", false, 'm'); + android::g_devCount = 1; + if (android_name_to_log_id("system") == LOG_ID_SYSTEM) { + dev->next = new log_device_t("system", false, 's'); + if (dev->next) { + dev = dev->next; + android::g_devCount++; + } + } + if (android_name_to_log_id("radio") == LOG_ID_RADIO) { + dev->next = new log_device_t("radio", false, 'r'); + if (dev->next) { + dev = dev->next; + android::g_devCount++; + } + } + if (android_name_to_log_id("events") == LOG_ID_EVENTS) { + dev->next = new log_device_t("events", true, 'e'); + if (dev->next) { + dev = dev->next; + android::g_devCount++; + needBinary = true; + } + } + if (android_name_to_log_id("crash") == LOG_ID_CRASH) { + dev->next = new log_device_t("crash", false, 'c'); + if (dev->next) { + android::g_devCount++; + } + } + break; + } + bool binary = strcmp(optarg, "events") == 0; if (binary) { needBinary = true; @@ -370,9 +513,6 @@ int main(int argc, char **argv) android::g_logRotateSizeKBytes = DEFAULT_LOG_ROTATE_SIZE_KBYTES; } else { - long logRotateSize; - char *lastDigit; - if (!isdigit(optarg[0])) { fprintf(stderr,"Invalid parameter to -r\n"); android::show_help(argv[0]); @@ -470,6 +610,10 @@ int main(int argc, char **argv) } break; + case 'S': + printStatistics = 1; + break; + default: fprintf(stderr,"Unrecognized Option\n"); android::show_help(argv[0]); @@ -479,10 +623,14 @@ int main(int argc, char **argv) } if (!devices) { - devices = new log_device_t("main", false, 'm'); + dev = devices = new log_device_t("main", false, 'm'); android::g_devCount = 1; if (android_name_to_log_id("system") == LOG_ID_SYSTEM) { - devices->next = new log_device_t("system", false, 's'); + dev = dev->next = new log_device_t("system", false, 's'); + android::g_devCount++; + } + if (android_name_to_log_id("crash") == LOG_ID_CRASH) { + dev = dev->next = new log_device_t("crash", false, 'c'); android::g_devCount++; } } @@ -544,7 +692,11 @@ int main(int argc, char **argv) } dev = devices; - logger_list = android_logger_list_alloc(mode, tail_lines, 0); + if (tail_time != log_time::EPOCH) { + logger_list = android_logger_list_alloc_time(mode, tail_time, 0); + } else { + logger_list = android_logger_list_alloc(mode, tail_lines, 0); + } while (dev) { dev->logger_list = logger_list; dev->logger = android_logger_open(logger_list, @@ -558,38 +710,117 @@ int main(int argc, char **argv) int ret; ret = android_logger_clear(dev->logger); if (ret) { - perror("clearLog"); + perror("failed to clear the log"); exit(EXIT_FAILURE); } } + if (setLogSize && android_logger_set_log_size(dev->logger, setLogSize)) { + perror("failed to set the log size"); + exit(EXIT_FAILURE); + } + if (getLogSize) { - int size, readable; + long size, readable; size = android_logger_get_log_size(dev->logger); if (size < 0) { - perror("getLogSize"); + perror("failed to get the log size"); exit(EXIT_FAILURE); } readable = android_logger_get_log_readable_size(dev->logger); if (readable < 0) { - perror("getLogReadableSize"); + perror("failed to get the readable log size"); exit(EXIT_FAILURE); } - printf("%s: ring buffer is %dKb (%dKb consumed), " + printf("%s: ring buffer is %ld%sb (%ld%sb consumed), " "max entry is %db, max payload is %db\n", dev->device, - size / 1024, readable / 1024, + value_of_size(size), multiplier_of_size(size), + value_of_size(readable), multiplier_of_size(readable), (int) LOGGER_ENTRY_MAX_LEN, (int) LOGGER_ENTRY_MAX_PAYLOAD); } dev = dev->next; } + if (setPruneList) { + size_t len = strlen(setPruneList) + 32; // margin to allow rc + char *buf = (char *) malloc(len); + + strcpy(buf, setPruneList); + int ret = android_logger_set_prune_list(logger_list, buf, len); + free(buf); + + if (ret) { + perror("failed to set the prune list"); + exit(EXIT_FAILURE); + } + } + + if (printStatistics || getPruneList) { + size_t len = 8192; + char *buf; + + for(int retry = 32; + (retry >= 0) && ((buf = new char [len])); + delete [] buf, --retry) { + if (getPruneList) { + android_logger_get_prune_list(logger_list, buf, len); + } else { + android_logger_get_statistics(logger_list, buf, len); + } + buf[len-1] = '\0'; + size_t ret = atol(buf) + 1; + if (ret < 4) { + delete [] buf; + buf = NULL; + break; + } + bool check = ret <= len; + len = ret; + if (check) { + break; + } + } + + if (!buf) { + perror("failed to read data"); + exit(EXIT_FAILURE); + } + + // remove trailing FF + char *cp = buf + len - 1; + *cp = '\0'; + bool truncated = *--cp != '\f'; + if (!truncated) { + *cp = '\0'; + } + + // squash out the byte count + cp = buf; + if (!truncated) { + while (isdigit(*cp)) { + ++cp; + } + if (*cp == '\n') { + ++cp; + } + } + + printf("%s", cp); + delete [] buf; + exit(0); + } + + if (getLogSize) { exit(0); } + if (setLogSize || setPruneList) { + exit(0); + } if (clearLog) { exit(0); } @@ -623,7 +854,7 @@ int main(int argc, char **argv) fprintf(stderr, "read: unexpected length.\n"); exit(EXIT_FAILURE); } - perror("logcat read"); + perror("logcat read failure"); exit(EXIT_FAILURE); } diff --git a/logcat/tests/Android.mk b/logcat/tests/Android.mk index bdaec14..015a23d 100644 --- a/logcat/tests/Android.mk +++ b/logcat/tests/Android.mk @@ -16,11 +16,7 @@ LOCAL_PATH := $(call my-dir) -# ----------------------------------------------------------------------------- -# Unit tests. -# ----------------------------------------------------------------------------- - -test_module := logcat-unit-tests +test_module_prefix := logcat- test_tags := tests test_c_flags := \ @@ -29,6 +25,28 @@ test_c_flags := \ -Wall -Wextra \ -Werror \ -fno-builtin \ + -std=gnu++11 + +# ----------------------------------------------------------------------------- +# Benchmarks (actually a gTest where the result code does not matter) +# ---------------------------------------------------------------------------- + +benchmark_src_files := \ + logcat_benchmark.cpp + +# Build benchmarks for the device. Run with: +# adb shell /data/nativetest/logcat-benchmarks/logcat-benchmarks +include $(CLEAR_VARS) +LOCAL_MODULE := $(test_module_prefix)benchmarks +LOCAL_MODULE_TAGS := $(test_tags) +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk +LOCAL_CFLAGS += $(test_c_flags) +LOCAL_SRC_FILES := $(benchmark_src_files) +include $(BUILD_NATIVE_TEST) + +# ----------------------------------------------------------------------------- +# Unit tests. +# ----------------------------------------------------------------------------- test_src_files := \ logcat_test.cpp \ @@ -36,11 +54,10 @@ test_src_files := \ # Build tests for the device (with .so). Run with: # adb shell /data/nativetest/logcat-unit-tests/logcat-unit-tests include $(CLEAR_VARS) -LOCAL_MODULE := $(test_module) +LOCAL_MODULE := $(test_module_prefix)unit-tests LOCAL_MODULE_TAGS := $(test_tags) LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk LOCAL_CFLAGS += $(test_c_flags) -LOCAL_LDLIBS := -lpthread LOCAL_SHARED_LIBRARIES := liblog LOCAL_SRC_FILES := $(test_src_files) include $(BUILD_NATIVE_TEST) diff --git a/logcat/tests/logcat_benchmark.cpp b/logcat/tests/logcat_benchmark.cpp new file mode 100644 index 0000000..be815be --- /dev/null +++ b/logcat/tests/logcat_benchmark.cpp @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2013-2014 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 <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <gtest/gtest.h> + +static const char begin[] = "--------- beginning of "; + +TEST(logcat, sorted_order) { + FILE *fp; + + ASSERT_TRUE(NULL != (fp = popen( + "logcat -v time -b radio -b events -b system -b main -d 2>/dev/null", + "r"))); + + class timestamp { + private: + int month; + int day; + int hour; + int minute; + int second; + int millisecond; + bool ok; + + public: + void init(const char *buffer) + { + ok = false; + if (buffer != NULL) { + ok = sscanf(buffer, "%d-%d %d:%d:%d.%d ", + &month, &day, &hour, &minute, &second, &millisecond) == 6; + } + } + + timestamp(const char *buffer) + { + init(buffer); + } + + bool operator< (timestamp &T) + { + return !ok || !T.ok + || (month < T.month) + || ((month == T.month) + && ((day < T.day) + || ((day == T.day) + && ((hour < T.hour) + || ((hour == T.hour) + && ((minute < T.minute) + || ((minute == T.minute) + && ((second < T.second) + || ((second == T.second) + && (millisecond < T.millisecond)))))))))); + } + + bool valid(void) + { + return ok; + } + } last(NULL); + + char *last_buffer = NULL; + char buffer[5120]; + + int count = 0; + int next_lt_last = 0; + + while (fgets(buffer, sizeof(buffer), fp)) { + if (!strncmp(begin, buffer, sizeof(begin) - 1)) { + continue; + } + if (!last.valid()) { + free(last_buffer); + last_buffer = strdup(buffer); + last.init(buffer); + } + timestamp next(buffer); + if (next < last) { + if (last_buffer) { + fprintf(stderr, "<%s", last_buffer); + } + fprintf(stderr, ">%s", buffer); + ++next_lt_last; + } + if (next.valid()) { + free(last_buffer); + last_buffer = strdup(buffer); + last.init(buffer); + } + ++count; + } + free(last_buffer); + + pclose(fp); + + static const int max_ok = 2; + + // Allow few fails, happens with readers active + fprintf(stderr, "%s: %d/%d out of order entries\n", + (next_lt_last) + ? ((next_lt_last <= max_ok) + ? "WARNING" + : "ERROR") + : "INFO", + next_lt_last, count); + + EXPECT_GE(max_ok, next_lt_last); + + // sample statistically too small + EXPECT_LT(100, count); +} diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp index f963a3a..9b316d1 100644 --- a/logcat/tests/logcat_test.cpp +++ b/logcat/tests/logcat_test.cpp @@ -14,8 +14,12 @@ * limitations under the License. */ +#include <ctype.h> #include <signal.h> #include <stdio.h> +#include <stdlib.h> +#include <string.h> + #include <gtest/gtest.h> #include <log/log.h> #include <log/logger.h> @@ -38,88 +42,10 @@ static const char begin[] = "--------- beginning of "; -TEST(logcat, sorted_order) { - FILE *fp; - - ASSERT_EQ(0, NULL == (fp = popen( - "logcat -v time -b radio -b events -b system -b main -d 2>/dev/null", - "r"))); - - class timestamp { - private: - int month; - int day; - int hour; - int minute; - int second; - int millisecond; - bool ok; - - public: - void init(const char *buffer) - { - ok = false; - if (buffer != NULL) { - ok = sscanf(buffer, "%d-%d %d:%d:%d.%d ", - &month, &day, &hour, &minute, &second, &millisecond) == 6; - } - } - - timestamp(const char *buffer) - { - init(buffer); - } - - bool operator< (timestamp &T) - { - return !ok || !T.ok - || (month < T.month) - || ((month == T.month) - && ((day < T.day) - || ((day == T.day) - && ((hour < T.hour) - || ((hour == T.hour) - && ((minute < T.minute) - || ((minute == T.minute) - && ((second < T.second) - || ((second == T.second) - && (millisecond < T.millisecond)))))))))); - } - - bool valid(void) - { - return ok; - } - } last(NULL); - - char buffer[5120]; - - int count = 0; - - while (fgets(buffer, sizeof(buffer), fp)) { - if (!strncmp(begin, buffer, sizeof(begin) - 1)) { - continue; - } - if (!last.valid()) { - last.init(buffer); - } - timestamp next(buffer); - ASSERT_EQ(0, next < last); - if (next.valid()) { - last.init(buffer); - } - ++count; - } - - pclose(fp); - - ASSERT_LT(100, count); -} - TEST(logcat, buckets) { FILE *fp; - ASSERT_EQ(0, NULL == (fp = popen( + ASSERT_TRUE(NULL != (fp = popen( "logcat -b radio -b events -b system -b main -d 2>/dev/null", "r"))); @@ -141,15 +67,15 @@ TEST(logcat, buckets) { pclose(fp); - ASSERT_EQ(15, ids); + EXPECT_EQ(15, ids); - ASSERT_EQ(4, count); + EXPECT_EQ(4, count); } TEST(logcat, tail_3) { FILE *fp; - ASSERT_EQ(0, NULL == (fp = popen( + ASSERT_TRUE(NULL != (fp = popen( "logcat -v long -b radio -b events -b system -b main -t 3 2>/dev/null", "r"))); @@ -173,7 +99,7 @@ TEST(logcat, tail_3) { TEST(logcat, tail_10) { FILE *fp; - ASSERT_EQ(0, NULL == (fp = popen( + ASSERT_TRUE(NULL != (fp = popen( "logcat -v long -b radio -b events -b system -b main -t 10 2>/dev/null", "r"))); @@ -197,7 +123,7 @@ TEST(logcat, tail_10) { TEST(logcat, tail_100) { FILE *fp; - ASSERT_EQ(0, NULL == (fp = popen( + ASSERT_TRUE(NULL != (fp = popen( "logcat -v long -b radio -b events -b system -b main -t 100 2>/dev/null", "r"))); @@ -221,7 +147,7 @@ TEST(logcat, tail_100) { TEST(logcat, tail_1000) { FILE *fp; - ASSERT_EQ(0, NULL == (fp = popen( + ASSERT_TRUE(NULL != (fp = popen( "logcat -v long -b radio -b events -b system -b main -t 1000 2>/dev/null", "r"))); @@ -242,6 +168,72 @@ TEST(logcat, tail_1000) { ASSERT_EQ(1000, count); } +TEST(logcat, tail_time) { + FILE *fp; + + ASSERT_TRUE(NULL != (fp = popen("logcat -v long -b all -t 10 2>&1", "r"))); + + char buffer[5120]; + char *last_timestamp = NULL; + char *first_timestamp = NULL; + int count = 0; + const unsigned int time_length = 18; + const unsigned int time_offset = 2; + + while (fgets(buffer, sizeof(buffer), fp)) { + if ((buffer[0] == '[') && (buffer[1] == ' ') + && isdigit(buffer[time_offset]) && isdigit(buffer[time_offset + 1]) + && (buffer[time_offset + 2] == '-')) { + ++count; + buffer[time_length + time_offset] = '\0'; + if (!first_timestamp) { + first_timestamp = strdup(buffer + time_offset); + } + free(last_timestamp); + last_timestamp = strdup(buffer + time_offset); + } + } + pclose(fp); + + EXPECT_EQ(10, count); + EXPECT_TRUE(last_timestamp != NULL); + EXPECT_TRUE(first_timestamp != NULL); + + snprintf(buffer, sizeof(buffer), "logcat -v long -b all -t '%s' 2>&1", + first_timestamp); + ASSERT_TRUE(NULL != (fp = popen(buffer, "r"))); + + int second_count = 0; + int last_timestamp_count = -1; + + while (fgets(buffer, sizeof(buffer), fp)) { + if ((buffer[0] == '[') && (buffer[1] == ' ') + && isdigit(buffer[time_offset]) && isdigit(buffer[time_offset + 1]) + && (buffer[time_offset + 2] == '-')) { + ++second_count; + buffer[time_length + time_offset] = '\0'; + if (first_timestamp) { + // we can get a transitory *extremely* rare failure if hidden + // underneath the time is *exactly* XX-XX XX:XX:XX.XXX000000 + EXPECT_STREQ(buffer + time_offset, first_timestamp); + free(first_timestamp); + first_timestamp = NULL; + } + if (!strcmp(buffer + time_offset, last_timestamp)) { + last_timestamp_count = second_count; + } + } + } + pclose(fp); + + free(last_timestamp); + last_timestamp = NULL; + + EXPECT_TRUE(first_timestamp == NULL); + EXPECT_LE(count, second_count); + EXPECT_LE(count, last_timestamp_count); +} + TEST(logcat, End_to_End) { pid_t pid = getpid(); @@ -250,7 +242,7 @@ TEST(logcat, End_to_End) { ASSERT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts))); FILE *fp; - ASSERT_EQ(0, NULL == (fp = popen( + ASSERT_TRUE(NULL != (fp = popen( "logcat -b events -t 100 2>/dev/null", "r"))); @@ -278,10 +270,11 @@ TEST(logcat, End_to_End) { ASSERT_EQ(1, count); } -TEST(logcat, get_) { +TEST(logcat, get_size) { FILE *fp; - ASSERT_EQ(0, NULL == (fp = popen( + // NB: crash log only available in user space + ASSERT_TRUE(NULL != (fp = popen( "logcat -b radio -b events -b system -b main -g 2>/dev/null", "r"))); @@ -291,13 +284,49 @@ TEST(logcat, get_) { while (fgets(buffer, sizeof(buffer), fp)) { int size, consumed, max, payload; + char size_mult, consumed_mult; + long full_size, full_consumed; size = consumed = max = payload = 0; - if ((4 == sscanf(buffer, "%*s ring buffer is %dKb (%dKb consumed)," - " max entry is %db, max payload is %db", - &size, &consumed, &max, &payload)) - && ((size * 3) >= consumed) - && ((size * 1024) > max) + // NB: crash log can be very small, not hit a Kb of consumed space + // doubly lucky we are not including it. + if (6 != sscanf(buffer, "%*s ring buffer is %d%cb (%d%cb consumed)," + " max entry is %db, max payload is %db", + &size, &size_mult, &consumed, &consumed_mult, + &max, &payload)) { + fprintf(stderr, "WARNING: Parse error: %s", buffer); + continue; + } + full_size = size; + switch(size_mult) { + case 'G': + full_size *= 1024; + /* FALLTHRU */ + case 'M': + full_size *= 1024; + /* FALLTHRU */ + case 'K': + full_size *= 1024; + break; + } + full_consumed = consumed; + switch(consumed_mult) { + case 'G': + full_consumed *= 1024; + /* FALLTHRU */ + case 'M': + full_consumed *= 1024; + /* FALLTHRU */ + case 'K': + full_consumed *= 1024; + break; + } + EXPECT_GT((full_size * 9) / 4, full_consumed); + EXPECT_GT(full_size, max); + EXPECT_GT(max, payload); + + if ((((full_size * 9) / 4) >= full_consumed) + && (full_size > max) && (max > payload)) { ++count; } @@ -308,7 +337,7 @@ TEST(logcat, get_) { ASSERT_EQ(4, count); } -static void caught_blocking(int signum) +static void caught_blocking(int /*signum*/) { unsigned long long v = 0xDEADBEEFA55A0000ULL; @@ -319,13 +348,17 @@ static void caught_blocking(int signum) TEST(logcat, blocking) { FILE *fp; - unsigned long long v = 0xDEADBEEFA55A0000ULL; + unsigned long long v = 0xDEADBEEFA55F0000ULL; pid_t pid = getpid(); v += pid & 0xFFFF; - ASSERT_EQ(0, NULL == (fp = popen( + LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v))); + + v &= 0xFFFFFFFFFFFAFFFFULL; + + ASSERT_TRUE(NULL != (fp = popen( "( trap exit HUP QUIT INT PIPE KILL ; sleep 6; echo DONE )&" " logcat -b events 2>&1", "r"))); @@ -339,14 +372,13 @@ TEST(logcat, blocking) { signal(SIGALRM, caught_blocking); alarm(2); while (fgets(buffer, sizeof(buffer), fp)) { - alarm(2); - - ++count; if (!strncmp(buffer, "DONE", 4)) { break; } + ++count; + int p; unsigned long long l; @@ -369,12 +401,12 @@ TEST(logcat, blocking) { pclose(fp); - ASSERT_LT(10, count); + EXPECT_LE(2, count); - ASSERT_EQ(1, signals); + EXPECT_EQ(1, signals); } -static void caught_blocking_tail(int signum) +static void caught_blocking_tail(int /*signum*/) { unsigned long long v = 0xA55ADEADBEEF0000ULL; @@ -385,13 +417,17 @@ static void caught_blocking_tail(int signum) TEST(logcat, blocking_tail) { FILE *fp; - unsigned long long v = 0xA55ADEADBEEF0000ULL; + unsigned long long v = 0xA55FDEADBEEF0000ULL; pid_t pid = getpid(); v += pid & 0xFFFF; - ASSERT_EQ(0, NULL == (fp = popen( + LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v))); + + v &= 0xFFFAFFFFFFFFFFFFULL; + + ASSERT_TRUE(NULL != (fp = popen( "( trap exit HUP QUIT INT PIPE KILL ; sleep 6; echo DONE )&" " logcat -b events -T 5 2>&1", "r"))); @@ -405,14 +441,13 @@ TEST(logcat, blocking_tail) { signal(SIGALRM, caught_blocking_tail); alarm(2); while (fgets(buffer, sizeof(buffer), fp)) { - alarm(2); - - ++count; if (!strncmp(buffer, "DONE", 4)) { break; } + ++count; + int p; unsigned long long l; @@ -431,13 +466,186 @@ TEST(logcat, blocking_tail) { alarm(0); signal(SIGALRM, SIG_DFL); - /* Generate SIGPIPE */ + // Generate SIGPIPE fclose(fp); caught_blocking_tail(0); pclose(fp); - ASSERT_LT(5, count); + EXPECT_LE(2, count); + + EXPECT_EQ(1, signals); +} + +static void caught_blocking_clear(int /*signum*/) +{ + unsigned long long v = 0xDEADBEEFA55C0000ULL; + + v += getpid() & 0xFFFF; + + LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v))); +} + +TEST(logcat, blocking_clear) { + FILE *fp; + unsigned long long v = 0xDEADBEEFA55C0000ULL; + + pid_t pid = getpid(); + + v += pid & 0xFFFF; + + // This test is racey; an event occurs between clear and dump. + // We accept that we will get a false positive, but never a false negative. + ASSERT_TRUE(NULL != (fp = popen( + "( trap exit HUP QUIT INT PIPE KILL ; sleep 6; echo DONE )&" + " logcat -b events -c 2>&1 ;" + " logcat -b events 2>&1", + "r"))); + + char buffer[5120]; + + int count = 0; + + int signals = 0; + + signal(SIGALRM, caught_blocking_clear); + alarm(2); + while (fgets(buffer, sizeof(buffer), fp)) { + + if (!strncmp(buffer, "clearLog: ", 10)) { + fprintf(stderr, "WARNING: Test lacks permission to run :-(\n"); + count = signals = 1; + break; + } + + if (!strncmp(buffer, "DONE", 4)) { + break; + } + + ++count; + + int p; + unsigned long long l; + + if ((2 != sscanf(buffer, "I/[0] ( %u): %lld", &p, &l)) + || (p != pid)) { + continue; + } + + if (l == v) { + if (count > 1) { + fprintf(stderr, "WARNING: Possible false positive\n"); + } + ++signals; + break; + } + } + alarm(0); + signal(SIGALRM, SIG_DFL); + + // Generate SIGPIPE + fclose(fp); + caught_blocking_clear(0); + + pclose(fp); + + EXPECT_LE(1, count); + + EXPECT_EQ(1, signals); +} + +static bool get_white_black(char **list) { + FILE *fp; + + fp = popen("logcat -p 2>/dev/null", "r"); + if (fp == NULL) { + fprintf(stderr, "ERROR: logcat -p 2>/dev/null\n"); + return false; + } + + char buffer[5120]; + + while (fgets(buffer, sizeof(buffer), fp)) { + char *hold = *list; + char *buf = buffer; + while (isspace(*buf)) { + ++buf; + } + char *end = buf + strlen(buf); + while (isspace(*--end) && (end >= buf)) { + *end = '\0'; + } + if (end < buf) { + continue; + } + if (hold) { + asprintf(list, "%s %s", hold, buf); + free(hold); + } else { + asprintf(list, "%s", buf); + } + } + pclose(fp); + return *list != NULL; +} + +static bool set_white_black(const char *list) { + FILE *fp; + + char buffer[5120]; + + snprintf(buffer, sizeof(buffer), "logcat -P '%s' 2>&1", list ? list : ""); + fp = popen(buffer, "r"); + if (fp == NULL) { + fprintf(stderr, "ERROR: %s\n", buffer); + return false; + } + + while (fgets(buffer, sizeof(buffer), fp)) { + char *buf = buffer; + while (isspace(*buf)) { + ++buf; + } + char *end = buf + strlen(buf); + while ((end > buf) && isspace(*--end)) { + *end = '\0'; + } + if (end <= buf) { + continue; + } + fprintf(stderr, "%s\n", buf); + pclose(fp); + return false; + } + return pclose(fp) == 0; +} - ASSERT_EQ(1, signals); +TEST(logcat, white_black_adjust) { + char *list = NULL; + char *adjust = NULL; + + get_white_black(&list); + + static const char adjustment[] = "~! 300/20 300/25 2000 ~1000/5 ~1000/30"; + ASSERT_EQ(true, set_white_black(adjustment)); + ASSERT_EQ(true, get_white_black(&adjust)); + EXPECT_STREQ(adjustment, adjust); + free(adjust); + adjust = NULL; + + static const char adjustment2[] = "300/20 300/21 2000 ~1000"; + ASSERT_EQ(true, set_white_black(adjustment2)); + ASSERT_EQ(true, get_white_black(&adjust)); + EXPECT_STREQ(adjustment2, adjust); + free(adjust); + adjust = NULL; + + ASSERT_EQ(true, set_white_black(list)); + get_white_black(&adjust); + EXPECT_STREQ(list ? list : "", adjust ? adjust : ""); + free(adjust); + adjust = NULL; + + free(list); + list = NULL; } diff --git a/logd/Android.mk b/logd/Android.mk new file mode 100644 index 0000000..188511f --- /dev/null +++ b/logd/Android.mk @@ -0,0 +1,33 @@ +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE:= logd + +LOCAL_SRC_FILES := \ + main.cpp \ + LogCommand.cpp \ + CommandListener.cpp \ + LogListener.cpp \ + LogReader.cpp \ + FlushCommand.cpp \ + LogBuffer.cpp \ + LogBufferElement.cpp \ + LogTimes.cpp \ + LogStatistics.cpp \ + LogWhiteBlackList.cpp \ + libaudit.c \ + LogAudit.cpp \ + event.logtags + +LOCAL_SHARED_LIBRARIES := \ + libsysutils \ + liblog \ + libcutils \ + libutils + +LOCAL_CFLAGS := -Werror $(shell sed -n 's/^\([0-9]*\)[ \t]*auditd[ \t].*/-DAUDITD_LOG_TAG=\1/p' $(LOCAL_PATH)/event.logtags) + +include $(BUILD_EXECUTABLE) + +include $(call first-makefiles-under,$(LOCAL_PATH)) diff --git a/logd/CommandListener.cpp b/logd/CommandListener.cpp new file mode 100644 index 0000000..d7088b4 --- /dev/null +++ b/logd/CommandListener.cpp @@ -0,0 +1,310 @@ +/* + * Copyright (C) 2012-2014 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 <arpa/inet.h> +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <netinet/in.h> +#include <string.h> +#include <stdlib.h> +#include <sys/prctl.h> +#include <sys/socket.h> +#include <sys/types.h> + +#include <cutils/sockets.h> +#include <private/android_filesystem_config.h> +#include <sysutils/SocketClient.h> + +#include "CommandListener.h" +#include "LogCommand.h" + +CommandListener::CommandListener(LogBuffer *buf, LogReader * /*reader*/, + LogListener * /*swl*/) + : FrameworkListener(getLogSocket()) + , mBuf(*buf) { + // registerCmd(new ShutdownCmd(buf, writer, swl)); + registerCmd(new ClearCmd(buf)); + registerCmd(new GetBufSizeCmd(buf)); + registerCmd(new SetBufSizeCmd(buf)); + registerCmd(new GetBufSizeUsedCmd(buf)); + registerCmd(new GetStatisticsCmd(buf)); + registerCmd(new SetPruneListCmd(buf)); + registerCmd(new GetPruneListCmd(buf)); +} + +CommandListener::ShutdownCmd::ShutdownCmd(LogBuffer *buf, LogReader *reader, + LogListener *swl) + : LogCommand("shutdown") + , mBuf(*buf) + , mReader(*reader) + , mSwl(*swl) +{ } + +int CommandListener::ShutdownCmd::runCommand(SocketClient * /*cli*/, + int /*argc*/, + char ** /*argv*/) { + mSwl.stopListener(); + mReader.stopListener(); + exit(0); +} + +CommandListener::ClearCmd::ClearCmd(LogBuffer *buf) + : LogCommand("clear") + , mBuf(*buf) +{ } + +static void setname() { + prctl(PR_SET_NAME, "logd.control"); +} + +int CommandListener::ClearCmd::runCommand(SocketClient *cli, + int argc, char **argv) { + setname(); + uid_t uid = cli->getUid(); + if (clientHasLogCredentials(cli)) { + uid = AID_ROOT; + } + + if (argc < 2) { + cli->sendMsg("Missing Argument"); + return 0; + } + + int id = atoi(argv[1]); + if ((id < LOG_ID_MIN) || (LOG_ID_MAX <= id)) { + cli->sendMsg("Range Error"); + return 0; + } + + mBuf.clear((log_id_t) id, uid); + cli->sendMsg("success"); + return 0; +} + +CommandListener::GetBufSizeCmd::GetBufSizeCmd(LogBuffer *buf) + : LogCommand("getLogSize") + , mBuf(*buf) +{ } + +int CommandListener::GetBufSizeCmd::runCommand(SocketClient *cli, + int argc, char **argv) { + setname(); + if (argc < 2) { + cli->sendMsg("Missing Argument"); + return 0; + } + + int id = atoi(argv[1]); + if ((id < LOG_ID_MIN) || (LOG_ID_MAX <= id)) { + cli->sendMsg("Range Error"); + return 0; + } + + unsigned long size = mBuf.getSize((log_id_t) id); + char buf[512]; + snprintf(buf, sizeof(buf), "%lu", size); + cli->sendMsg(buf); + return 0; +} + +CommandListener::SetBufSizeCmd::SetBufSizeCmd(LogBuffer *buf) + : LogCommand("setLogSize") + , mBuf(*buf) +{ } + +int CommandListener::SetBufSizeCmd::runCommand(SocketClient *cli, + int argc, char **argv) { + setname(); + if (!clientHasLogCredentials(cli)) { + cli->sendMsg("Permission Denied"); + return 0; + } + + if (argc < 3) { + cli->sendMsg("Missing Argument"); + return 0; + } + + int id = atoi(argv[1]); + if ((id < LOG_ID_MIN) || (LOG_ID_MAX <= id)) { + cli->sendMsg("Range Error"); + return 0; + } + + unsigned long size = atol(argv[2]); + if (mBuf.setSize((log_id_t) id, size)) { + cli->sendMsg("Range Error"); + return 0; + } + + cli->sendMsg("success"); + return 0; +} + +CommandListener::GetBufSizeUsedCmd::GetBufSizeUsedCmd(LogBuffer *buf) + : LogCommand("getLogSizeUsed") + , mBuf(*buf) +{ } + +int CommandListener::GetBufSizeUsedCmd::runCommand(SocketClient *cli, + int argc, char **argv) { + setname(); + if (argc < 2) { + cli->sendMsg("Missing Argument"); + return 0; + } + + int id = atoi(argv[1]); + if ((id < LOG_ID_MIN) || (LOG_ID_MAX <= id)) { + cli->sendMsg("Range Error"); + return 0; + } + + unsigned long size = mBuf.getSizeUsed((log_id_t) id); + char buf[512]; + snprintf(buf, sizeof(buf), "%lu", size); + cli->sendMsg(buf); + return 0; +} + +CommandListener::GetStatisticsCmd::GetStatisticsCmd(LogBuffer *buf) + : LogCommand("getStatistics") + , mBuf(*buf) +{ } + +static void package_string(char **strp) { + const char *a = *strp; + if (!a) { + a = ""; + } + + // Calculate total buffer size prefix, count is the string length w/o nul + char fmt[32]; + for(size_t l = strlen(a), y = 0, x = 6; y != x; y = x, x = strlen(fmt) - 2) { + snprintf(fmt, sizeof(fmt), "%zu\n%%s\n\f", l + x); + } + + char *b = *strp; + *strp = NULL; + asprintf(strp, fmt, a); + free(b); +} + +int CommandListener::GetStatisticsCmd::runCommand(SocketClient *cli, + int argc, char **argv) { + setname(); + uid_t uid = cli->getUid(); + if (clientHasLogCredentials(cli)) { + uid = AID_ROOT; + } + + unsigned int logMask = -1; + if (argc > 1) { + logMask = 0; + for (int i = 1; i < argc; ++i) { + int id = atoi(argv[i]); + if ((id < LOG_ID_MIN) || (LOG_ID_MAX <= id)) { + cli->sendMsg("Range Error"); + return 0; + } + logMask |= 1 << id; + } + } + + char *buf = NULL; + + mBuf.formatStatistics(&buf, uid, logMask); + if (!buf) { + cli->sendMsg("Failed"); + } else { + package_string(&buf); + cli->sendMsg(buf); + free(buf); + } + return 0; +} + +CommandListener::GetPruneListCmd::GetPruneListCmd(LogBuffer *buf) + : LogCommand("getPruneList") + , mBuf(*buf) +{ } + +int CommandListener::GetPruneListCmd::runCommand(SocketClient *cli, + int /*argc*/, char ** /*argv*/) { + setname(); + char *buf = NULL; + mBuf.formatPrune(&buf); + if (!buf) { + cli->sendMsg("Failed"); + } else { + package_string(&buf); + cli->sendMsg(buf); + free(buf); + } + return 0; +} + +CommandListener::SetPruneListCmd::SetPruneListCmd(LogBuffer *buf) + : LogCommand("setPruneList") + , mBuf(*buf) +{ } + +int CommandListener::SetPruneListCmd::runCommand(SocketClient *cli, + int argc, char **argv) { + setname(); + if (!clientHasLogCredentials(cli)) { + cli->sendMsg("Permission Denied"); + return 0; + } + + char *cp = NULL; + for (int i = 1; i < argc; ++i) { + char *p = cp; + if (p) { + cp = NULL; + asprintf(&cp, "%s %s", p, argv[i]); + free(p); + } else { + asprintf(&cp, "%s", argv[i]); + } + } + + int ret = mBuf.initPrune(cp); + free(cp); + + if (ret) { + cli->sendMsg("Invalid"); + return 0; + } + + cli->sendMsg("success"); + + return 0; +} + +int CommandListener::getLogSocket() { + static const char socketName[] = "logd"; + int sock = android_get_control_socket(socketName); + + if (sock < 0) { + sock = socket_local_server(socketName, + ANDROID_SOCKET_NAMESPACE_RESERVED, + SOCK_STREAM); + } + + return sock; +} diff --git a/logd/CommandListener.h b/logd/CommandListener.h new file mode 100644 index 0000000..cd1c306 --- /dev/null +++ b/logd/CommandListener.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2012-2014 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. + */ + +#ifndef _COMMANDLISTENER_H__ +#define _COMMANDLISTENER_H__ + +#include <sysutils/FrameworkListener.h> +#include "LogCommand.h" +#include "LogBuffer.h" +#include "LogReader.h" +#include "LogListener.h" + +class CommandListener : public FrameworkListener { + LogBuffer &mBuf; + +public: + CommandListener(LogBuffer *buf, LogReader *reader, LogListener *swl); + virtual ~CommandListener() {} + +private: + static int getLogSocket(); + + class ShutdownCmd : public LogCommand { + LogBuffer &mBuf; + LogReader &mReader; + LogListener &mSwl; + + public: + ShutdownCmd(LogBuffer *buf, LogReader *reader, LogListener *swl); + virtual ~ShutdownCmd() {} + int runCommand(SocketClient *c, int argc, char ** argv); + }; + +#define LogBufferCmd(name) \ + class name##Cmd : public LogCommand { \ + LogBuffer &mBuf; \ + public: \ + name##Cmd(LogBuffer *buf); \ + virtual ~name##Cmd() {} \ + int runCommand(SocketClient *c, int argc, char ** argv); \ + }; + + LogBufferCmd(Clear) + LogBufferCmd(GetBufSize) + LogBufferCmd(SetBufSize) + LogBufferCmd(GetBufSizeUsed) + LogBufferCmd(GetStatistics) + LogBufferCmd(GetPruneList) + LogBufferCmd(SetPruneList) +}; + +#endif diff --git a/logd/FlushCommand.cpp b/logd/FlushCommand.cpp new file mode 100644 index 0000000..3be07c0 --- /dev/null +++ b/logd/FlushCommand.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2012-2014 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 <stdlib.h> + +#include "FlushCommand.h" +#include "LogBufferElement.h" +#include "LogCommand.h" +#include "LogReader.h" +#include "LogTimes.h" + +FlushCommand::FlushCommand(LogReader &reader, + bool nonBlock, + unsigned long tail, + unsigned int logMask, + pid_t pid, + log_time start) + : mReader(reader) + , mNonBlock(nonBlock) + , mTail(tail) + , mLogMask(logMask) + , mPid(pid) + , mStart(start) +{ } + +// runSocketCommand is called once for every open client on the +// log reader socket. Here we manage and associated the reader +// client tracking and log region locks LastLogTimes list of +// LogTimeEntrys, and spawn a transitory per-client thread to +// work at filing data to the socket. +// +// global LogTimeEntry::lock() is used to protect access, +// reference counts are used to ensure that individual +// LogTimeEntry lifetime is managed when not protected. +void FlushCommand::runSocketCommand(SocketClient *client) { + LogTimeEntry *entry = NULL; + LastLogTimes × = mReader.logbuf().mTimes; + + LogTimeEntry::lock(); + LastLogTimes::iterator it = times.begin(); + while(it != times.end()) { + entry = (*it); + if (entry->mClient == client) { + entry->triggerReader_Locked(); + if (entry->runningReader_Locked()) { + LogTimeEntry::unlock(); + return; + } + entry->incRef_Locked(); + break; + } + it++; + } + + if (it == times.end()) { + // Create LogTimeEntry in notifyNewLog() ? + if (mTail == (unsigned long) -1) { + LogTimeEntry::unlock(); + return; + } + entry = new LogTimeEntry(mReader, client, mNonBlock, mTail, mLogMask, mPid, mStart); + times.push_back(entry); + } + + client->incRef(); + + // release client and entry reference counts once done + entry->startReader_Locked(); + LogTimeEntry::unlock(); +} + +bool FlushCommand::hasReadLogs(SocketClient *client) { + return clientHasLogCredentials(client); +} diff --git a/logd/FlushCommand.h b/logd/FlushCommand.h new file mode 100644 index 0000000..f34c06a --- /dev/null +++ b/logd/FlushCommand.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2012-2013 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. + */ +#ifndef _FLUSH_COMMAND_H +#define _FLUSH_COMMAND_H + +#include <log/log_read.h> +#include <sysutils/SocketClientCommand.h> + +class LogBufferElement; + +#include "LogTimes.h" + +class LogReader; + +class FlushCommand : public SocketClientCommand { + LogReader &mReader; + bool mNonBlock; + unsigned long mTail; + unsigned int mLogMask; + pid_t mPid; + log_time mStart; + +public: + FlushCommand(LogReader &mReader, + bool nonBlock = false, + unsigned long tail = -1, + unsigned int logMask = -1, + pid_t pid = 0, + log_time start = LogTimeEntry::EPOCH); + virtual void runSocketCommand(SocketClient *client); + + static bool hasReadLogs(SocketClient *client); +}; + +#endif diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp new file mode 100644 index 0000000..f8d6162 --- /dev/null +++ b/logd/LogAudit.cpp @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2014 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 <ctype.h> +#include <errno.h> +#include <limits.h> +#include <stdarg.h> +#include <stdlib.h> +#include <sys/klog.h> +#include <sys/prctl.h> +#include <sys/uio.h> + +#include "libaudit.h" +#include "LogAudit.h" + +LogAudit::LogAudit(LogBuffer *buf, LogReader *reader, int fdDmsg) + : SocketListener(getLogSocket(), false) + , logbuf(buf) + , reader(reader) + , fdDmesg(-1) { + logDmesg(); + fdDmesg = fdDmsg; +} + +bool LogAudit::onDataAvailable(SocketClient *cli) { + prctl(PR_SET_NAME, "logd.auditd"); + + struct audit_message rep; + + rep.nlh.nlmsg_type = 0; + rep.nlh.nlmsg_len = 0; + rep.data[0] = '\0'; + + if (audit_get_reply(cli->getSocket(), &rep, GET_REPLY_BLOCKING, 0) < 0) { + SLOGE("Failed on audit_get_reply with error: %s", strerror(errno)); + return false; + } + + logPrint("type=%d %.*s", rep.nlh.nlmsg_type, rep.nlh.nlmsg_len, rep.data); + + return true; +} + +int LogAudit::logPrint(const char *fmt, ...) { + if (fmt == NULL) { + return -EINVAL; + } + + va_list args; + + char *str = NULL; + va_start(args, fmt); + int rc = vasprintf(&str, fmt, args); + va_end(args); + + if (rc < 0) { + return rc; + } + + char *cp; + while ((cp = strstr(str, " "))) { + memmove(cp, cp + 1, strlen(cp + 1) + 1); + } + + if (fdDmesg >= 0) { + struct iovec iov[2]; + + iov[0].iov_base = str; + iov[0].iov_len = strlen(str); + iov[1].iov_base = const_cast<char *>("\n"); + iov[1].iov_len = 1; + + writev(fdDmesg, iov, sizeof(iov) / sizeof(iov[0])); + } + + pid_t pid = getpid(); + pid_t tid = gettid(); + uid_t uid = getuid(); + log_time now; + + static const char audit_str[] = " audit("; + char *timeptr = strstr(str, audit_str); + if (timeptr + && ((cp = now.strptime(timeptr + sizeof(audit_str) - 1, "%s.%q"))) + && (*cp == ':')) { + memcpy(timeptr + sizeof(audit_str) - 1, "0.0", 3); + memmove(timeptr + sizeof(audit_str) - 1 + 3, cp, strlen(cp) + 1); + } else { + now.strptime("", ""); // side effect of setting CLOCK_REALTIME + } + + static const char pid_str[] = " pid="; + char *pidptr = strstr(str, pid_str); + if (pidptr && isdigit(pidptr[sizeof(pid_str) - 1])) { + cp = pidptr + sizeof(pid_str) - 1; + pid = 0; + while (isdigit(*cp)) { + pid = (pid * 10) + (*cp - '0'); + ++cp; + } + tid = pid; + uid = logbuf->pidToUid(pid); + memmove(pidptr, cp, strlen(cp) + 1); + } + + // log to events + + size_t l = strlen(str); + size_t n = l + sizeof(uint32_t) + sizeof(uint8_t) + sizeof(uint32_t); + + bool notify = false; + + char *newstr = reinterpret_cast<char *>(malloc(n)); + if (!newstr) { + rc = -ENOMEM; + } else { + cp = newstr; + *cp++ = AUDITD_LOG_TAG & 0xFF; + *cp++ = (AUDITD_LOG_TAG >> 8) & 0xFF; + *cp++ = (AUDITD_LOG_TAG >> 16) & 0xFF; + *cp++ = (AUDITD_LOG_TAG >> 24) & 0xFF; + *cp++ = EVENT_TYPE_STRING; + *cp++ = l & 0xFF; + *cp++ = (l >> 8) & 0xFF; + *cp++ = (l >> 16) & 0xFF; + *cp++ = (l >> 24) & 0xFF; + memcpy(cp, str, l); + + logbuf->log(LOG_ID_EVENTS, now, uid, pid, tid, newstr, + (n <= USHRT_MAX) ? (unsigned short) n : USHRT_MAX); + free(newstr); + + notify = true; + } + + // log to main + + static const char comm_str[] = " comm=\""; + const char *comm = strstr(str, comm_str); + const char *estr = str + strlen(str); + if (comm) { + estr = comm; + comm += sizeof(comm_str) - 1; + } else if (pid == getpid()) { + pid = tid; + comm = "auditd"; + } else if (!(comm = logbuf->pidToName(pid))) { + comm = "unknown"; + } + + const char *ecomm = strchr(comm, '"'); + if (ecomm) { + ++ecomm; + l = ecomm - comm; + } else { + l = strlen(comm) + 1; + ecomm = ""; + } + n = (estr - str) + strlen(ecomm) + l + 2; + + newstr = reinterpret_cast<char *>(malloc(n)); + if (!newstr) { + rc = -ENOMEM; + } else { + *newstr = (strstr(str, " permissive=1") + || strstr(str, " policy loaded ")) + ? ANDROID_LOG_INFO + : ANDROID_LOG_WARN; + strlcpy(newstr + 1, comm, l); + strncpy(newstr + 1 + l, str, estr - str); + strcpy(newstr + 1 + l + (estr - str), ecomm); + + logbuf->log(LOG_ID_MAIN, now, uid, pid, tid, newstr, + (n <= USHRT_MAX) ? (unsigned short) n : USHRT_MAX); + free(newstr); + + notify = true; + } + + free(str); + + if (notify) { + reader->notifyNewLog(); + } + + return rc; +} + +void LogAudit::logDmesg() { + int len = klogctl(KLOG_SIZE_BUFFER, NULL, 0); + if (len <= 0) { + return; + } + + len++; + char buf[len]; + + int rc = klogctl(KLOG_READ_ALL, buf, len); + + buf[len - 1] = '\0'; + + for(char *tok = buf; (rc >= 0) && ((tok = strtok(tok, "\r\n"))); tok = NULL) { + char *audit = strstr(tok, " audit("); + if (!audit) { + continue; + } + + *audit++ = '\0'; + + char *type = strstr(tok, "type="); + if (type) { + rc = logPrint("%s %s", type, audit); + } else { + rc = logPrint("%s", audit); + } + } +} + +int LogAudit::getLogSocket() { + int fd = audit_open(); + if (fd < 0) { + return fd; + } + if (audit_set_pid(fd, getpid(), WAIT_YES) < 0) { + audit_close(fd); + fd = -1; + } + return fd; +} diff --git a/logd/LogAudit.h b/logd/LogAudit.h new file mode 100644 index 0000000..111030a --- /dev/null +++ b/logd/LogAudit.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2014 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. + */ + +#ifndef _LOGD_LOG_AUDIT_H__ +#define _LOGD_LOG_AUDIT_H__ + +#include <sysutils/SocketListener.h> +#include "LogReader.h" + +class LogAudit : public SocketListener { + LogBuffer *logbuf; + LogReader *reader; + int fdDmesg; + +public: + LogAudit(LogBuffer *buf, LogReader *reader, int fdDmesg); + +protected: + virtual bool onDataAvailable(SocketClient *cli); + +private: + static int getLogSocket(); + void logDmesg(); + int logPrint(const char *fmt, ...) + __attribute__ ((__format__ (__printf__, 2, 3))); +}; + +#endif diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp new file mode 100644 index 0000000..cd9ea20 --- /dev/null +++ b/logd/LogBuffer.cpp @@ -0,0 +1,503 @@ +/* + * Copyright (C) 2012-2014 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 <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/user.h> +#include <time.h> +#include <unistd.h> + +#include <cutils/properties.h> +#include <log/logger.h> + +#include "LogBuffer.h" +#include "LogReader.h" +#include "LogStatistics.h" +#include "LogWhiteBlackList.h" + +// Default +#define LOG_BUFFER_SIZE (256 * 1024) // Tuned on a per-platform basis here? +#define log_buffer_size(id) mMaxSize[id] +#define LOG_BUFFER_MIN_SIZE (64 * 1024UL) +#define LOG_BUFFER_MAX_SIZE (256 * 1024 * 1024UL) + +static bool valid_size(unsigned long value) { + if ((value < LOG_BUFFER_MIN_SIZE) || (LOG_BUFFER_MAX_SIZE < value)) { + return false; + } + + long pages = sysconf(_SC_PHYS_PAGES); + if (pages < 1) { + return true; + } + + long pagesize = sysconf(_SC_PAGESIZE); + if (pagesize <= 1) { + pagesize = PAGE_SIZE; + } + + // maximum memory impact a somewhat arbitrary ~3% + pages = (pages + 31) / 32; + unsigned long maximum = pages * pagesize; + + if ((maximum < LOG_BUFFER_MIN_SIZE) || (LOG_BUFFER_MAX_SIZE < maximum)) { + return true; + } + + return value <= maximum; +} + +static unsigned long property_get_size(const char *key) { + char property[PROPERTY_VALUE_MAX]; + property_get(key, property, ""); + + char *cp; + unsigned long value = strtoul(property, &cp, 10); + + switch(*cp) { + case 'm': + case 'M': + value *= 1024; + /* FALLTHRU */ + case 'k': + case 'K': + value *= 1024; + /* FALLTHRU */ + case '\0': + break; + + default: + value = 0; + } + + if (!valid_size(value)) { + value = 0; + } + + return value; +} + +LogBuffer::LogBuffer(LastLogTimes *times) + : mTimes(*times) { + pthread_mutex_init(&mLogElementsLock, NULL); + dgram_qlen_statistics = false; + + static const char global_tuneable[] = "persist.logd.size"; // Settings App + static const char global_default[] = "ro.logd.size"; // BoardConfig.mk + + unsigned long default_size = property_get_size(global_tuneable); + if (!default_size) { + default_size = property_get_size(global_default); + } + + log_id_for_each(i) { + char key[PROP_NAME_MAX]; + + snprintf(key, sizeof(key), "%s.%s", + global_tuneable, android_log_id_to_name(i)); + unsigned long property_size = property_get_size(key); + + if (!property_size) { + snprintf(key, sizeof(key), "%s.%s", + global_default, android_log_id_to_name(i)); + property_size = property_get_size(key); + } + + if (!property_size) { + property_size = default_size; + } + + if (!property_size) { + property_size = LOG_BUFFER_SIZE; + } + + if (setSize(i, property_size)) { + setSize(i, LOG_BUFFER_MIN_SIZE); + } + } +} + +void LogBuffer::log(log_id_t log_id, log_time realtime, + uid_t uid, pid_t pid, pid_t tid, + const char *msg, unsigned short len) { + if ((log_id >= LOG_ID_MAX) || (log_id < 0)) { + return; + } + LogBufferElement *elem = new LogBufferElement(log_id, realtime, + uid, pid, tid, msg, len); + + pthread_mutex_lock(&mLogElementsLock); + + // Insert elements in time sorted order if possible + // NB: if end is region locked, place element at end of list + LogBufferElementCollection::iterator it = mLogElements.end(); + LogBufferElementCollection::iterator last = it; + while (--it != mLogElements.begin()) { + if ((*it)->getRealTime() <= realtime) { + // halves the peak performance, use with caution + if (dgram_qlen_statistics) { + LogBufferElementCollection::iterator ib = it; + unsigned short buckets, num = 1; + for (unsigned short i = 0; (buckets = stats.dgram_qlen(i)); ++i) { + buckets -= num; + num += buckets; + while (buckets && (--ib != mLogElements.begin())) { + --buckets; + } + if (buckets) { + break; + } + stats.recordDiff( + elem->getRealTime() - (*ib)->getRealTime(), i); + } + } + break; + } + last = it; + } + + if (last == mLogElements.end()) { + mLogElements.push_back(elem); + } else { + log_time end = log_time::EPOCH; + bool end_set = false; + bool end_always = false; + + LogTimeEntry::lock(); + + LastLogTimes::iterator t = mTimes.begin(); + while(t != mTimes.end()) { + LogTimeEntry *entry = (*t); + if (entry->owned_Locked()) { + if (!entry->mNonBlock) { + end_always = true; + break; + } + if (!end_set || (end <= entry->mEnd)) { + end = entry->mEnd; + end_set = true; + } + } + t++; + } + + if (end_always + || (end_set && (end >= (*last)->getMonotonicTime()))) { + mLogElements.push_back(elem); + } else { + mLogElements.insert(last,elem); + } + + LogTimeEntry::unlock(); + } + + stats.add(len, log_id, uid, pid); + maybePrune(log_id); + pthread_mutex_unlock(&mLogElementsLock); +} + +// If we're using more than 256K of memory for log entries, prune +// at least 10% of the log entries. +// +// mLogElementsLock must be held when this function is called. +void LogBuffer::maybePrune(log_id_t id) { + size_t sizes = stats.sizes(id); + if (sizes > log_buffer_size(id)) { + size_t sizeOver90Percent = sizes - ((log_buffer_size(id) * 9) / 10); + size_t elements = stats.elements(id); + unsigned long pruneRows = elements * sizeOver90Percent / sizes; + elements /= 10; + if (pruneRows <= elements) { + pruneRows = elements; + } + prune(id, pruneRows); + } +} + +// prune "pruneRows" of type "id" from the buffer. +// +// mLogElementsLock must be held when this function is called. +void LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) { + LogTimeEntry *oldest = NULL; + + LogTimeEntry::lock(); + + // Region locked? + LastLogTimes::iterator t = mTimes.begin(); + while(t != mTimes.end()) { + LogTimeEntry *entry = (*t); + if (entry->owned_Locked() + && (!oldest || (oldest->mStart > entry->mStart))) { + oldest = entry; + } + t++; + } + + LogBufferElementCollection::iterator it; + + if (caller_uid != AID_ROOT) { + for(it = mLogElements.begin(); it != mLogElements.end();) { + LogBufferElement *e = *it; + + if (oldest && (oldest->mStart <= e->getMonotonicTime())) { + break; + } + + if (e->getLogId() != id) { + ++it; + continue; + } + + uid_t uid = e->getUid(); + + if (uid == caller_uid) { + it = mLogElements.erase(it); + unsigned short len = e->getMsgLen(); + stats.subtract(len, id, uid, e->getPid()); + delete e; + pruneRows--; + if (pruneRows == 0) { + break; + } + } else { + ++it; + } + } + LogTimeEntry::unlock(); + return; + } + + // prune by worst offender by uid + while (pruneRows > 0) { + // recalculate the worst offender on every batched pass + uid_t worst = (uid_t) -1; + size_t worst_sizes = 0; + size_t second_worst_sizes = 0; + + if ((id != LOG_ID_CRASH) && mPrune.worstUidEnabled()) { + LidStatistics &l = stats.id(id); + l.sort(); + UidStatisticsCollection::iterator iu = l.begin(); + if (iu != l.end()) { + UidStatistics *u = *iu; + worst = u->getUid(); + worst_sizes = u->sizes(); + if (++iu != l.end()) { + second_worst_sizes = (*iu)->sizes(); + } + } + } + + bool kick = false; + for(it = mLogElements.begin(); it != mLogElements.end();) { + LogBufferElement *e = *it; + + if (oldest && (oldest->mStart <= e->getMonotonicTime())) { + break; + } + + if (e->getLogId() != id) { + ++it; + continue; + } + + uid_t uid = e->getUid(); + + if (uid == worst) { + it = mLogElements.erase(it); + unsigned short len = e->getMsgLen(); + stats.subtract(len, id, worst, e->getPid()); + delete e; + kick = true; + pruneRows--; + if ((pruneRows == 0) || (worst_sizes < second_worst_sizes)) { + break; + } + worst_sizes -= len; + } else if (mPrune.naughty(e)) { // BlackListed + it = mLogElements.erase(it); + stats.subtract(e->getMsgLen(), id, uid, e->getPid()); + delete e; + pruneRows--; + if (pruneRows == 0) { + break; + } + } else { + ++it; + } + } + + if (!kick || !mPrune.worstUidEnabled()) { + break; // the following loop will ask bad clients to skip/drop + } + } + + bool whitelist = false; + it = mLogElements.begin(); + while((pruneRows > 0) && (it != mLogElements.end())) { + LogBufferElement *e = *it; + if (e->getLogId() == id) { + if (oldest && (oldest->mStart <= e->getMonotonicTime())) { + if (!whitelist) { + if (stats.sizes(id) > (2 * log_buffer_size(id))) { + // kick a misbehaving log reader client off the island + oldest->release_Locked(); + } else { + oldest->triggerSkip_Locked(pruneRows); + } + } + break; + } + + if (mPrune.nice(e)) { // WhiteListed + whitelist = true; + it++; + continue; + } + + it = mLogElements.erase(it); + stats.subtract(e->getMsgLen(), id, e->getUid(), e->getPid()); + delete e; + pruneRows--; + } else { + it++; + } + } + + if (whitelist && (pruneRows > 0)) { + it = mLogElements.begin(); + while((it != mLogElements.end()) && (pruneRows > 0)) { + LogBufferElement *e = *it; + if (e->getLogId() == id) { + if (oldest && (oldest->mStart <= e->getMonotonicTime())) { + if (stats.sizes(id) > (2 * log_buffer_size(id))) { + // kick a misbehaving log reader client off the island + oldest->release_Locked(); + } else { + oldest->triggerSkip_Locked(pruneRows); + } + break; + } + it = mLogElements.erase(it); + stats.subtract(e->getMsgLen(), id, e->getUid(), e->getPid()); + delete e; + pruneRows--; + } else { + it++; + } + } + } + + LogTimeEntry::unlock(); +} + +// clear all rows of type "id" from the buffer. +void LogBuffer::clear(log_id_t id, uid_t uid) { + pthread_mutex_lock(&mLogElementsLock); + prune(id, ULONG_MAX, uid); + pthread_mutex_unlock(&mLogElementsLock); +} + +// get the used space associated with "id". +unsigned long LogBuffer::getSizeUsed(log_id_t id) { + pthread_mutex_lock(&mLogElementsLock); + size_t retval = stats.sizes(id); + pthread_mutex_unlock(&mLogElementsLock); + return retval; +} + +// set the total space allocated to "id" +int LogBuffer::setSize(log_id_t id, unsigned long size) { + // Reasonable limits ... + if (!valid_size(size)) { + return -1; + } + pthread_mutex_lock(&mLogElementsLock); + log_buffer_size(id) = size; + pthread_mutex_unlock(&mLogElementsLock); + return 0; +} + +// get the total space allocated to "id" +unsigned long LogBuffer::getSize(log_id_t id) { + pthread_mutex_lock(&mLogElementsLock); + size_t retval = log_buffer_size(id); + pthread_mutex_unlock(&mLogElementsLock); + return retval; +} + +log_time LogBuffer::flushTo( + SocketClient *reader, const log_time start, bool privileged, + bool (*filter)(const LogBufferElement *element, void *arg), void *arg) { + LogBufferElementCollection::iterator it; + log_time max = start; + uid_t uid = reader->getUid(); + + pthread_mutex_lock(&mLogElementsLock); + for (it = mLogElements.begin(); it != mLogElements.end(); ++it) { + LogBufferElement *element = *it; + + if (!privileged && (element->getUid() != uid)) { + continue; + } + + if (element->getMonotonicTime() <= start) { + continue; + } + + // NB: calling out to another object with mLogElementsLock held (safe) + if (filter && !(*filter)(element, arg)) { + continue; + } + + pthread_mutex_unlock(&mLogElementsLock); + + // range locking in LastLogTimes looks after us + max = element->flushTo(reader); + + if (max == element->FLUSH_ERROR) { + return max; + } + + pthread_mutex_lock(&mLogElementsLock); + } + pthread_mutex_unlock(&mLogElementsLock); + + return max; +} + +void LogBuffer::formatStatistics(char **strp, uid_t uid, unsigned int logMask) { + log_time oldest(CLOCK_MONOTONIC); + + pthread_mutex_lock(&mLogElementsLock); + + // Find oldest element in the log(s) + LogBufferElementCollection::iterator it; + for (it = mLogElements.begin(); it != mLogElements.end(); ++it) { + LogBufferElement *element = *it; + + if ((logMask & (1 << element->getLogId()))) { + oldest = element->getMonotonicTime(); + break; + } + } + + stats.format(strp, uid, logMask, oldest); + + pthread_mutex_unlock(&mLogElementsLock); +} diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h new file mode 100644 index 0000000..4b982a8 --- /dev/null +++ b/logd/LogBuffer.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2012-2014 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. + */ + +#ifndef _LOGD_LOG_BUFFER_H__ +#define _LOGD_LOG_BUFFER_H__ + +#include <sys/types.h> + +#include <log/log.h> +#include <sysutils/SocketClient.h> +#include <utils/List.h> + +#include <private/android_filesystem_config.h> + +#include "LogBufferElement.h" +#include "LogTimes.h" +#include "LogStatistics.h" +#include "LogWhiteBlackList.h" + +typedef android::List<LogBufferElement *> LogBufferElementCollection; + +class LogBuffer { + LogBufferElementCollection mLogElements; + pthread_mutex_t mLogElementsLock; + + LogStatistics stats; + + bool dgram_qlen_statistics; + + PruneList mPrune; + + unsigned long mMaxSize[LOG_ID_MAX]; + +public: + LastLogTimes &mTimes; + + LogBuffer(LastLogTimes *times); + + void log(log_id_t log_id, log_time realtime, + uid_t uid, pid_t pid, pid_t tid, + const char *msg, unsigned short len); + log_time flushTo(SocketClient *writer, const log_time start, + bool privileged, + bool (*filter)(const LogBufferElement *element, void *arg) = NULL, + void *arg = NULL); + + void clear(log_id_t id, uid_t uid = AID_ROOT); + unsigned long getSize(log_id_t id); + int setSize(log_id_t id, unsigned long size); + unsigned long getSizeUsed(log_id_t id); + // *strp uses malloc, use free to release. + void formatStatistics(char **strp, uid_t uid, unsigned int logMask); + + void enableDgramQlenStatistics() { + stats.enableDgramQlenStatistics(); + dgram_qlen_statistics = true; + } + + int initPrune(char *cp) { return mPrune.init(cp); } + // *strp uses malloc, use free to release. + void formatPrune(char **strp) { mPrune.format(strp); } + + // helper + char *pidToName(pid_t pid) { return stats.pidToName(pid); } + uid_t pidToUid(pid_t pid) { return stats.pidToUid(pid); } + +private: + void maybePrune(log_id_t id); + void prune(log_id_t id, unsigned long pruneRows, uid_t uid = AID_ROOT); + +}; + +#endif // _LOGD_LOG_BUFFER_H__ diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp new file mode 100644 index 0000000..d959ceb --- /dev/null +++ b/logd/LogBufferElement.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2012-2014 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 <stdio.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#include <log/logger.h> + +#include "LogBufferElement.h" +#include "LogReader.h" + +const log_time LogBufferElement::FLUSH_ERROR((uint32_t)0, (uint32_t)0); + +LogBufferElement::LogBufferElement(log_id_t log_id, log_time realtime, + uid_t uid, pid_t pid, pid_t tid, + const char *msg, unsigned short len) + : mLogId(log_id) + , mUid(uid) + , mPid(pid) + , mTid(tid) + , mMsgLen(len) + , mMonotonicTime(CLOCK_MONOTONIC) + , mRealTime(realtime) { + mMsg = new char[len]; + memcpy(mMsg, msg, len); +} + +LogBufferElement::~LogBufferElement() { + delete [] mMsg; +} + +log_time LogBufferElement::flushTo(SocketClient *reader) { + struct logger_entry_v3 entry; + memset(&entry, 0, sizeof(struct logger_entry_v3)); + entry.hdr_size = sizeof(struct logger_entry_v3); + entry.len = mMsgLen; + entry.lid = mLogId; + entry.pid = mPid; + entry.tid = mTid; + entry.sec = mRealTime.tv_sec; + entry.nsec = mRealTime.tv_nsec; + + struct iovec iovec[2]; + iovec[0].iov_base = &entry; + iovec[0].iov_len = sizeof(struct logger_entry_v3); + iovec[1].iov_base = mMsg; + iovec[1].iov_len = mMsgLen; + if (reader->sendDatav(iovec, 2)) { + return FLUSH_ERROR; + } + + return mMonotonicTime; +} diff --git a/logd/LogBufferElement.h b/logd/LogBufferElement.h new file mode 100644 index 0000000..fdca973 --- /dev/null +++ b/logd/LogBufferElement.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2012-2014 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. + */ + +#ifndef _LOGD_LOG_BUFFER_ELEMENT_H__ +#define _LOGD_LOG_BUFFER_ELEMENT_H__ + +#include <sys/types.h> +#include <sysutils/SocketClient.h> +#include <log/log.h> +#include <log/log_read.h> + +class LogBufferElement { + const log_id_t mLogId; + const uid_t mUid; + const pid_t mPid; + const pid_t mTid; + char *mMsg; + const unsigned short mMsgLen; + const log_time mMonotonicTime; + const log_time mRealTime; + +public: + LogBufferElement(log_id_t log_id, log_time realtime, + uid_t uid, pid_t pid, pid_t tid, + const char *msg, unsigned short len); + virtual ~LogBufferElement(); + + log_id_t getLogId() const { return mLogId; } + uid_t getUid(void) const { return mUid; } + pid_t getPid(void) const { return mPid; } + pid_t getTid(void) const { return mTid; } + unsigned short getMsgLen() const { return mMsgLen; } + log_time getMonotonicTime(void) const { return mMonotonicTime; } + log_time getRealTime(void) const { return mRealTime; } + + static const log_time FLUSH_ERROR; + log_time flushTo(SocketClient *writer); +}; + +#endif diff --git a/logd/LogCommand.cpp b/logd/LogCommand.cpp new file mode 100644 index 0000000..e4c138e --- /dev/null +++ b/logd/LogCommand.cpp @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2012-2014 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 <stdlib.h> + +#include <private/android_filesystem_config.h> + +#include "LogCommand.h" + +LogCommand::LogCommand(const char *cmd) + : FrameworkCommand(cmd) { +} + +// gets a list of supplementary group IDs associated with +// the socket peer. This is implemented by opening +// /proc/PID/status and look for the "Group:" line. +// +// This function introduces races especially since status +// can change 'shape' while reading, the net result is err +// on lack of permission. +// +// Race-free alternative is to introduce pairs of sockets +// and threads for each command and reading, one each that +// has open permissions, and one that has restricted +// permissions. + +static bool groupIsLog(char *buf) { + char *ptr; + static const char ws[] = " \n"; + bool ret = false; + + for (buf = strtok_r(buf, ws, &ptr); buf; buf = strtok_r(NULL, ws, &ptr)) { + errno = 0; + gid_t Gid = strtol(buf, NULL, 10); + if (errno != 0) { + return false; + } + if (Gid == AID_LOG) { + ret = true; + } + } + return ret; +} + +bool clientHasLogCredentials(SocketClient * cli) { + uid_t uid = cli->getUid(); + if (uid == AID_ROOT) { + return true; + } + + gid_t gid = cli->getGid(); + if ((gid == AID_ROOT) || (gid == AID_SYSTEM) || (gid == AID_LOG)) { + return true; + } + + // FYI We will typically be here for 'adb logcat' + bool ret = false; + + char filename[1024]; + snprintf(filename, sizeof(filename), "/proc/%d/status", cli->getPid()); + + FILE *file = fopen(filename, "r"); + if (!file) { + return ret; + } + + bool foundGid = false; + bool foundUid = false; + + char line[1024]; + while (fgets(line, sizeof(line), file)) { + static const char groups_string[] = "Groups:\t"; + static const char uid_string[] = "Uid:\t"; + static const char gid_string[] = "Gid:\t"; + + if (strncmp(groups_string, line, strlen(groups_string)) == 0) { + ret = groupIsLog(line + strlen(groups_string)); + if (!ret) { + break; + } + } else if (strncmp(uid_string, line, strlen(uid_string)) == 0) { + uid_t u[4] = { (uid_t) -1, (uid_t) -1, (uid_t) -1, (uid_t) -1}; + + sscanf(line + strlen(uid_string), "%u\t%u\t%u\t%u", + &u[0], &u[1], &u[2], &u[3]); + + // Protect against PID reuse by checking that the UID is the same + if ((uid != u[0]) || (uid != u[1]) || (uid != u[2]) || (uid != u[3])) { + ret = false; + break; + } + foundUid = true; + } else if (strncmp(gid_string, line, strlen(gid_string)) == 0) { + gid_t g[4] = { (gid_t) -1, (gid_t) -1, (gid_t) -1, (gid_t) -1}; + + sscanf(line + strlen(gid_string), "%u\t%u\t%u\t%u", + &g[0], &g[1], &g[2], &g[3]); + + // Protect against PID reuse by checking that the GID is the same + if ((gid != g[0]) || (gid != g[1]) || (gid != g[2]) || (gid != g[3])) { + ret = false; + break; + } + foundGid = true; + } + } + + fclose(file); + + if (!foundGid || !foundUid) { + ret = false; + } + + return ret; +} diff --git a/logd/LogCommand.h b/logd/LogCommand.h new file mode 100644 index 0000000..e3b96a2 --- /dev/null +++ b/logd/LogCommand.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2012-2014 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. + */ + +#ifndef _LOGD_COMMAND_H +#define _LOGD_COMMAND_H + +#include <sysutils/SocketClient.h> +#include <sysutils/FrameworkCommand.h> + +class LogCommand : public FrameworkCommand { +public: + LogCommand(const char *cmd); + virtual ~LogCommand() {} +}; + +bool clientHasLogCredentials(SocketClient * cli); + +#endif diff --git a/logd/LogListener.cpp b/logd/LogListener.cpp new file mode 100644 index 0000000..8186cea --- /dev/null +++ b/logd/LogListener.cpp @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2012-2014 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 <limits.h> +#include <sys/prctl.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <sys/un.h> +#include <unistd.h> + +#include <cutils/sockets.h> +#include <log/logger.h> + +#include "LogListener.h" + +LogListener::LogListener(LogBuffer *buf, LogReader *reader) + : SocketListener(getLogSocket(), false) + , logbuf(buf) + , reader(reader) +{ } + +bool LogListener::onDataAvailable(SocketClient *cli) { + prctl(PR_SET_NAME, "logd.writer"); + + char buffer[sizeof_log_id_t + sizeof(uint16_t) + sizeof(log_time) + + LOGGER_ENTRY_MAX_PAYLOAD]; + struct iovec iov = { buffer, sizeof(buffer) }; + memset(buffer, 0, sizeof(buffer)); + + char control[CMSG_SPACE(sizeof(struct ucred))]; + struct msghdr hdr = { + NULL, + 0, + &iov, + 1, + control, + sizeof(control), + 0, + }; + + int socket = cli->getSocket(); + + ssize_t n = recvmsg(socket, &hdr, 0); + if (n <= (ssize_t)(sizeof_log_id_t + sizeof(uint16_t) + sizeof(log_time))) { + return false; + } + + struct ucred *cred = NULL; + + struct cmsghdr *cmsg = CMSG_FIRSTHDR(&hdr); + while (cmsg != NULL) { + if (cmsg->cmsg_level == SOL_SOCKET + && cmsg->cmsg_type == SCM_CREDENTIALS) { + cred = (struct ucred *)CMSG_DATA(cmsg); + break; + } + cmsg = CMSG_NXTHDR(&hdr, cmsg); + } + + if (cred == NULL) { + return false; + } + + if (cred->uid == getuid()) { + // ignore log messages we send to ourself. + // Such log messages are often generated by libraries we depend on + // which use standard Android logging. + return false; + } + + // First log element is always log_id. + log_id_t log_id = (log_id_t) *((typeof_log_id_t *) buffer); + if (log_id < 0 || log_id >= LOG_ID_MAX) { + return false; + } + char *msg = ((char *)buffer) + sizeof_log_id_t; + n -= sizeof_log_id_t; + + // second element is the thread id of the caller + pid_t tid = (pid_t) *((uint16_t *) msg); + msg += sizeof(uint16_t); + n -= sizeof(uint16_t); + + // third element is the realtime at point of caller + log_time realtime(msg); + msg += sizeof(log_time); + n -= sizeof(log_time); + + // NB: hdr.msg_flags & MSG_TRUNC is not tested, silently passing a + // truncated message to the logs. + + logbuf->log(log_id, realtime, cred->uid, cred->pid, tid, msg, + ((size_t) n <= USHRT_MAX) ? (unsigned short) n : USHRT_MAX); + reader->notifyNewLog(); + + return true; +} + +int LogListener::getLogSocket() { + static const char socketName[] = "logdw"; + int sock = android_get_control_socket(socketName); + + if (sock < 0) { + sock = socket_local_server(socketName, + ANDROID_SOCKET_NAMESPACE_RESERVED, + SOCK_DGRAM); + } + + int on = 1; + if (setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) { + return -1; + } + return sock; +} diff --git a/logd/LogListener.h b/logd/LogListener.h new file mode 100644 index 0000000..7099e13 --- /dev/null +++ b/logd/LogListener.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2012-2013 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. + */ + +#ifndef _LOGD_LOG_LISTENER_H__ +#define _LOGD_LOG_LISTENER_H__ + +#include <sysutils/SocketListener.h> +#include "LogReader.h" + +class LogListener : public SocketListener { + LogBuffer *logbuf; + LogReader *reader; + +public: + LogListener(LogBuffer *buf, LogReader *reader); + +protected: + virtual bool onDataAvailable(SocketClient *cli); + +private: + static int getLogSocket(); +}; + +#endif diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp new file mode 100644 index 0000000..8458c19 --- /dev/null +++ b/logd/LogReader.cpp @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2012-2013 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 <ctype.h> +#include <poll.h> +#include <sys/prctl.h> +#include <sys/socket.h> + +#include <cutils/sockets.h> + +#include "LogReader.h" +#include "FlushCommand.h" + +LogReader::LogReader(LogBuffer *logbuf) + : SocketListener(getLogSocket(), true) + , mLogbuf(*logbuf) +{ } + +// When we are notified a new log entry is available, inform +// all of our listening sockets. +void LogReader::notifyNewLog() { + FlushCommand command(*this); + runOnEachSocket(&command); +} + +bool LogReader::onDataAvailable(SocketClient *cli) { + prctl(PR_SET_NAME, "logd.reader"); + + char buffer[255]; + + int len = read(cli->getSocket(), buffer, sizeof(buffer) - 1); + if (len <= 0) { + doSocketDelete(cli); + return false; + } + buffer[len] = '\0'; + + unsigned long tail = 0; + static const char _tail[] = " tail="; + char *cp = strstr(buffer, _tail); + if (cp) { + tail = atol(cp + sizeof(_tail) - 1); + } + + log_time start(log_time::EPOCH); + static const char _start[] = " start="; + cp = strstr(buffer, _start); + if (cp) { + // Parse errors will result in current time + start.strptime(cp + sizeof(_start) - 1, "%s.%q"); + } + + unsigned int logMask = -1; + static const char _logIds[] = " lids="; + cp = strstr(buffer, _logIds); + if (cp) { + logMask = 0; + cp += sizeof(_logIds) - 1; + while (*cp && *cp != '\0') { + int val = 0; + while (isdigit(*cp)) { + val = val * 10 + *cp - '0'; + ++cp; + } + logMask |= 1 << val; + if (*cp != ',') { + break; + } + ++cp; + } + } + + pid_t pid = 0; + static const char _pid[] = " pid="; + cp = strstr(buffer, _pid); + if (cp) { + pid = atol(cp + sizeof(_pid) - 1); + } + + bool nonBlock = false; + if (strncmp(buffer, "dumpAndClose", 12) == 0) { + nonBlock = true; + } + + // Convert realtime to monotonic time + if (start == log_time::EPOCH) { + start = LogTimeEntry::EPOCH; + } else { + class LogFindStart { + const pid_t mPid; + const unsigned mLogMask; + bool startTimeSet; + log_time &start; + log_time last; + + public: + LogFindStart(unsigned logMask, pid_t pid, log_time &start) + : mPid(pid) + , mLogMask(logMask) + , startTimeSet(false) + , start(start) + , last(LogTimeEntry::EPOCH) + { } + + static bool callback(const LogBufferElement *element, void *obj) { + LogFindStart *me = reinterpret_cast<LogFindStart *>(obj); + if (!me->startTimeSet + && (!me->mPid || (me->mPid == element->getPid())) + && (me->mLogMask & (1 << element->getLogId()))) { + if (me->start == element->getRealTime()) { + me->start = element->getMonotonicTime(); + me->startTimeSet = true; + } else { + if (me->start < element->getRealTime()) { + me->start = me->last; + me->startTimeSet = true; + } + me->last = element->getMonotonicTime(); + } + } + return false; + } + + bool found() { return startTimeSet; } + } logFindStart(logMask, pid, start); + + logbuf().flushTo(cli, LogTimeEntry::EPOCH, + FlushCommand::hasReadLogs(cli), + logFindStart.callback, &logFindStart); + + if (!logFindStart.found()) { + if (nonBlock) { + doSocketDelete(cli); + return false; + } + log_time now(CLOCK_MONOTONIC); + start = now; + } + } + + FlushCommand command(*this, nonBlock, tail, logMask, pid, start); + command.runSocketCommand(cli); + return true; +} + +void LogReader::doSocketDelete(SocketClient *cli) { + LastLogTimes × = mLogbuf.mTimes; + LogTimeEntry::lock(); + LastLogTimes::iterator it = times.begin(); + while(it != times.end()) { + LogTimeEntry *entry = (*it); + if (entry->mClient == cli) { + times.erase(it); + entry->release_Locked(); + break; + } + it++; + } + LogTimeEntry::unlock(); +} + +int LogReader::getLogSocket() { + static const char socketName[] = "logdr"; + int sock = android_get_control_socket(socketName); + + if (sock < 0) { + sock = socket_local_server(socketName, + ANDROID_SOCKET_NAMESPACE_RESERVED, + SOCK_SEQPACKET); + } + + return sock; +} diff --git a/logd/LogReader.h b/logd/LogReader.h new file mode 100644 index 0000000..91559a3 --- /dev/null +++ b/logd/LogReader.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2012-2013 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. + */ + +#ifndef _LOGD_LOG_WRITER_H__ +#define _LOGD_LOG_WRITER_H__ + +#include <sysutils/SocketListener.h> +#include "LogBuffer.h" +#include "LogTimes.h" + +class LogReader : public SocketListener { + LogBuffer &mLogbuf; + +public: + LogReader(LogBuffer *logbuf); + void notifyNewLog(); + + LogBuffer &logbuf(void) const { return mLogbuf; } + +protected: + virtual bool onDataAvailable(SocketClient *cli); + +private: + static int getLogSocket(); + + void doSocketDelete(SocketClient *cli); + +}; + +#endif diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp new file mode 100644 index 0000000..81c9bab --- /dev/null +++ b/logd/LogStatistics.cpp @@ -0,0 +1,926 @@ +/* + * Copyright (C) 2014 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 <fcntl.h> +#include <stdarg.h> +#include <time.h> + +#include <log/logger.h> +#include <private/android_filesystem_config.h> +#include <utils/String8.h> + +#include "LogStatistics.h" + +PidStatistics::PidStatistics(pid_t pid, char *name) + : pid(pid) + , mSizesTotal(0) + , mElementsTotal(0) + , mSizes(0) + , mElements(0) + , name(name) + , mGone(false) +{ } + +#ifdef DO_NOT_ERROR_IF_PIDSTATISTICS_USES_A_COPY_CONSTRUCTOR +PidStatistics::PidStatistics(const PidStatistics ©) + : pid(copy->pid) + , name(copy->name ? strdup(copy->name) : NULL) + , mSizesTotal(copy->mSizesTotal) + , mElementsTotal(copy->mElementsTotal) + , mSizes(copy->mSizes) + , mElements(copy->mElements) + , mGone(copy->mGone) +{ } +#endif + +PidStatistics::~PidStatistics() { + free(name); +} + +bool PidStatistics::pidGone() { + if (mGone) { + return true; + } + if (pid == gone) { + return true; + } + if (kill(pid, 0) && (errno != EPERM)) { + mGone = true; + return true; + } + return false; +} + +void PidStatistics::setName(char *new_name) { + free(name); + name = new_name; +} + +void PidStatistics::add(unsigned short size) { + mSizesTotal += size; + ++mElementsTotal; + mSizes += size; + ++mElements; +} + +bool PidStatistics::subtract(unsigned short size) { + mSizes -= size; + --mElements; + return (mElements == 0) && pidGone(); +} + +void PidStatistics::addTotal(size_t size, size_t element) { + if (pid == gone) { + mSizesTotal += size; + mElementsTotal += element; + } +} + +// must call free to release return value +char *PidStatistics::pidToName(pid_t pid) { + char *retval = NULL; + if (pid != gone) { + char buffer[512]; + snprintf(buffer, sizeof(buffer), "/proc/%u/cmdline", pid); + int fd = open(buffer, O_RDONLY); + if (fd >= 0) { + ssize_t ret = read(fd, buffer, sizeof(buffer)); + if (ret > 0) { + buffer[sizeof(buffer)-1] = '\0'; + // frameworks intermediate state + if (strcmp(buffer, "<pre-initialized>")) { + retval = strdup(buffer); + } + } + close(fd); + } + } + return retval; +} + +UidStatistics::UidStatistics(uid_t uid) + : uid(uid) + , mSizes(0) + , mElements(0) { + Pids.clear(); +} + +UidStatistics::~UidStatistics() { + PidStatisticsCollection::iterator it; + for (it = begin(); it != end();) { + delete (*it); + it = Pids.erase(it); + } +} + +void UidStatistics::add(unsigned short size, pid_t pid) { + mSizes += size; + ++mElements; + + PidStatistics *p; + PidStatisticsCollection::iterator last; + PidStatisticsCollection::iterator it; + for (last = it = begin(); it != end(); last = it, ++it) { + p = *it; + if (pid == p->getPid()) { + p->add(size); + return; + } + } + // insert if the gone entry. + bool insert = (last != it) && (p->getPid() == p->gone); + p = new PidStatistics(pid, pidToName(pid)); + if (insert) { + Pids.insert(last, p); + } else { + Pids.push_back(p); + } + p->add(size); +} + +void UidStatistics::subtract(unsigned short size, pid_t pid) { + mSizes -= size; + --mElements; + + PidStatisticsCollection::iterator it; + for (it = begin(); it != end(); ++it) { + PidStatistics *p = *it; + if (pid == p->getPid()) { + if (p->subtract(size)) { + size_t szsTotal = p->sizesTotal(); + size_t elsTotal = p->elementsTotal(); + delete p; + Pids.erase(it); + it = end(); + --it; + if (it == end()) { + p = new PidStatistics(p->gone); + Pids.push_back(p); + } else { + p = *it; + if (p->getPid() != p->gone) { + p = new PidStatistics(p->gone); + Pids.push_back(p); + } + } + p->addTotal(szsTotal, elsTotal); + } + return; + } + } +} + +void UidStatistics::sort() { + for (bool pass = true; pass;) { + pass = false; + PidStatisticsCollection::iterator it = begin(); + if (it != end()) { + PidStatisticsCollection::iterator lt = it; + PidStatistics *l = (*lt); + while (++it != end()) { + PidStatistics *n = (*it); + if ((n->getPid() != n->gone) && (n->sizes() > l->sizes())) { + pass = true; + Pids.erase(it); + Pids.insert(lt, n); + it = lt; + n = l; + } + lt = it; + l = n; + } + } + } +} + +size_t UidStatistics::sizes(pid_t pid) { + if (pid == pid_all) { + return sizes(); + } + + PidStatisticsCollection::iterator it; + for (it = begin(); it != end(); ++it) { + PidStatistics *p = *it; + if (pid == p->getPid()) { + return p->sizes(); + } + } + return 0; +} + +size_t UidStatistics::elements(pid_t pid) { + if (pid == pid_all) { + return elements(); + } + + PidStatisticsCollection::iterator it; + for (it = begin(); it != end(); ++it) { + PidStatistics *p = *it; + if (pid == p->getPid()) { + return p->elements(); + } + } + return 0; +} + +size_t UidStatistics::sizesTotal(pid_t pid) { + size_t sizes = 0; + PidStatisticsCollection::iterator it; + for (it = begin(); it != end(); ++it) { + PidStatistics *p = *it; + if ((pid == pid_all) || (pid == p->getPid())) { + sizes += p->sizesTotal(); + } + } + return sizes; +} + +size_t UidStatistics::elementsTotal(pid_t pid) { + size_t elements = 0; + PidStatisticsCollection::iterator it; + for (it = begin(); it != end(); ++it) { + PidStatistics *p = *it; + if ((pid == pid_all) || (pid == p->getPid())) { + elements += p->elementsTotal(); + } + } + return elements; +} + +LidStatistics::LidStatistics() { + Uids.clear(); +} + +LidStatistics::~LidStatistics() { + UidStatisticsCollection::iterator it; + for (it = begin(); it != end();) { + delete (*it); + it = Uids.erase(it); + } +} + +void LidStatistics::add(unsigned short size, uid_t uid, pid_t pid) { + UidStatistics *u; + UidStatisticsCollection::iterator it; + UidStatisticsCollection::iterator last; + + if (uid == (uid_t) -1) { // init + uid = (uid_t) AID_ROOT; + } + + for (last = it = begin(); it != end(); last = it, ++it) { + u = *it; + if (uid == u->getUid()) { + u->add(size, pid); + if ((last != it) && ((*last)->sizesTotal() < u->sizesTotal())) { + Uids.erase(it); + Uids.insert(last, u); + } + return; + } + } + u = new UidStatistics(uid); + if ((last != it) && ((*last)->sizesTotal() < (size_t) size)) { + Uids.insert(last, u); + } else { + Uids.push_back(u); + } + u->add(size, pid); +} + +void LidStatistics::subtract(unsigned short size, uid_t uid, pid_t pid) { + UidStatisticsCollection::iterator it; + for (it = begin(); it != end(); ++it) { + UidStatistics *u = *it; + if (uid == u->getUid()) { + u->subtract(size, pid); + return; + } + } +} + +void LidStatistics::sort() { + for (bool pass = true; pass;) { + pass = false; + UidStatisticsCollection::iterator it = begin(); + if (it != end()) { + UidStatisticsCollection::iterator lt = it; + UidStatistics *l = (*lt); + while (++it != end()) { + UidStatistics *n = (*it); + if (n->sizes() > l->sizes()) { + pass = true; + Uids.erase(it); + Uids.insert(lt, n); + it = lt; + n = l; + } + lt = it; + l = n; + } + } + } +} + +size_t LidStatistics::sizes(uid_t uid, pid_t pid) { + size_t sizes = 0; + UidStatisticsCollection::iterator it; + for (it = begin(); it != end(); ++it) { + UidStatistics *u = *it; + if ((uid == uid_all) || (uid == u->getUid())) { + sizes += u->sizes(pid); + } + } + return sizes; +} + +size_t LidStatistics::elements(uid_t uid, pid_t pid) { + size_t elements = 0; + UidStatisticsCollection::iterator it; + for (it = begin(); it != end(); ++it) { + UidStatistics *u = *it; + if ((uid == uid_all) || (uid == u->getUid())) { + elements += u->elements(pid); + } + } + return elements; +} + +size_t LidStatistics::sizesTotal(uid_t uid, pid_t pid) { + size_t sizes = 0; + UidStatisticsCollection::iterator it; + for (it = begin(); it != end(); ++it) { + UidStatistics *u = *it; + if ((uid == uid_all) || (uid == u->getUid())) { + sizes += u->sizesTotal(pid); + } + } + return sizes; +} + +size_t LidStatistics::elementsTotal(uid_t uid, pid_t pid) { + size_t elements = 0; + UidStatisticsCollection::iterator it; + for (it = begin(); it != end(); ++it) { + UidStatistics *u = *it; + if ((uid == uid_all) || (uid == u->getUid())) { + elements += u->elementsTotal(pid); + } + } + return elements; +} + +LogStatistics::LogStatistics() + : start(CLOCK_MONOTONIC) { + log_id_for_each(i) { + mSizes[i] = 0; + mElements[i] = 0; + } + + dgram_qlen_statistics = false; + for(unsigned short bucket = 0; dgram_qlen(bucket); ++bucket) { + mMinimum[bucket].tv_sec = mMinimum[bucket].tv_sec_max; + mMinimum[bucket].tv_nsec = mMinimum[bucket].tv_nsec_max; + } +} + +// Each bucket below represents a dgram_qlen of log messages. By +// finding the minimum period of time from start to finish +// of each dgram_qlen, we can get a performance expectation for +// the user space logger. The net result is that the period +// of time divided by the dgram_qlen will give us the average time +// between log messages; at the point where the average time +// is greater than the throughput capability of the logger +// we will not longer require the benefits of the FIFO formed +// by max_dgram_qlen. We will also expect to see a very visible +// knee in the average time between log messages at this point, +// so we do not necessarily have to compare the rate against the +// measured performance (BM_log_maximum_retry) of the logger. +// +// for example (reformatted): +// +// Minimum time between log events per dgram_qlen: +// 1 2 3 5 10 20 30 50 100 200 300 400 500 600 +// 5u2 12u 13u 15u 16u 27u 30u 36u 407u 3m1 3m3 3m9 3m9 5m5 +// +// demonstrates a clear knee rising at 100, so this means that for this +// case max_dgram_qlen = 100 would be more than sufficient to handle the +// worst that the system could stuff into the logger. The +// BM_log_maximum_retry performance (derated by the log collection) on the +// same system was 33.2us so we would almost be fine with max_dgram_qlen = 50. +// BM_log_maxumum_retry with statistics off is roughly 20us, so +// max_dgram_qlen = 20 would work. We will be more than willing to have +// a large engineering margin so the rule of thumb that lead us to 100 is +// fine. +// +// bucket dgram_qlen are tuned for /proc/sys/net/unix/max_dgram_qlen = 300 +const unsigned short LogStatistics::mBuckets[] = { + 1, 2, 3, 5, 10, 20, 30, 50, 100, 200, 300, 400, 500, 600 +}; + +unsigned short LogStatistics::dgram_qlen(unsigned short bucket) { + if (bucket >= sizeof(mBuckets) / sizeof(mBuckets[0])) { + return 0; + } + return mBuckets[bucket]; +} + +unsigned long long LogStatistics::minimum(unsigned short bucket) { + if (mMinimum[bucket].tv_sec == mMinimum[bucket].tv_sec_max) { + return 0; + } + return mMinimum[bucket].nsec(); +} + +void LogStatistics::recordDiff(log_time diff, unsigned short bucket) { + if ((diff.tv_sec || diff.tv_nsec) && (mMinimum[bucket] > diff)) { + mMinimum[bucket] = diff; + } +} + +void LogStatistics::add(unsigned short size, + log_id_t log_id, uid_t uid, pid_t pid) { + mSizes[log_id] += size; + ++mElements[log_id]; + id(log_id).add(size, uid, pid); +} + +void LogStatistics::subtract(unsigned short size, + log_id_t log_id, uid_t uid, pid_t pid) { + mSizes[log_id] -= size; + --mElements[log_id]; + id(log_id).subtract(size, uid, pid); +} + +size_t LogStatistics::sizes(log_id_t log_id, uid_t uid, pid_t pid) { + if (log_id != log_id_all) { + return id(log_id).sizes(uid, pid); + } + size_t sizes = 0; + log_id_for_each(i) { + sizes += id(i).sizes(uid, pid); + } + return sizes; +} + +size_t LogStatistics::elements(log_id_t log_id, uid_t uid, pid_t pid) { + if (log_id != log_id_all) { + return id(log_id).elements(uid, pid); + } + size_t elements = 0; + log_id_for_each(i) { + elements += id(i).elements(uid, pid); + } + return elements; +} + +size_t LogStatistics::sizesTotal(log_id_t log_id, uid_t uid, pid_t pid) { + if (log_id != log_id_all) { + return id(log_id).sizesTotal(uid, pid); + } + size_t sizes = 0; + log_id_for_each(i) { + sizes += id(i).sizesTotal(uid, pid); + } + return sizes; +} + +size_t LogStatistics::elementsTotal(log_id_t log_id, uid_t uid, pid_t pid) { + if (log_id != log_id_all) { + return id(log_id).elementsTotal(uid, pid); + } + size_t elements = 0; + log_id_for_each(i) { + elements += id(i).elementsTotal(uid, pid); + } + return elements; +} + +void LogStatistics::format(char **buf, + uid_t uid, unsigned int logMask, log_time oldest) { + static const unsigned short spaces_current = 13; + static const unsigned short spaces_total = 19; + + if (*buf) { + free(*buf); + *buf = NULL; + } + + android::String8 string(" span -> size/num"); + size_t oldLength; + short spaces = 2; + + log_id_for_each(i) { + if (!logMask & (1 << i)) { + continue; + } + oldLength = string.length(); + if (spaces < 0) { + spaces = 0; + } + string.appendFormat("%*s%s", spaces, "", android_log_id_to_name(i)); + spaces += spaces_total + oldLength - string.length(); + + LidStatistics &l = id(i); + l.sort(); + + UidStatisticsCollection::iterator iu; + for (iu = l.begin(); iu != l.end(); ++iu) { + (*iu)->sort(); + } + } + + spaces = 1; + log_time t(CLOCK_MONOTONIC); + unsigned long long d = t.nsec() - start.nsec(); + string.appendFormat("\nTotal%4llu:%02llu:%02llu.%09llu", + d / NS_PER_SEC / 60 / 60, (d / NS_PER_SEC / 60) % 60, + (d / NS_PER_SEC) % 60, d % NS_PER_SEC); + + log_id_for_each(i) { + if (!(logMask & (1 << i))) { + continue; + } + oldLength = string.length(); + if (spaces < 0) { + spaces = 0; + } + string.appendFormat("%*s%zu/%zu", spaces, "", + sizesTotal(i), elementsTotal(i)); + spaces += spaces_total + oldLength - string.length(); + } + + spaces = 1; + d = t.nsec() - oldest.nsec(); + string.appendFormat("\nNow%6llu:%02llu:%02llu.%09llu", + d / NS_PER_SEC / 60 / 60, (d / NS_PER_SEC / 60) % 60, + (d / NS_PER_SEC) % 60, d % NS_PER_SEC); + + log_id_for_each(i) { + if (!(logMask & (1 << i))) { + continue; + } + + size_t els = elements(i); + if (els) { + oldLength = string.length(); + if (spaces < 0) { + spaces = 0; + } + string.appendFormat("%*s%zu/%zu", spaces, "", sizes(i), els); + spaces -= string.length() - oldLength; + } + spaces += spaces_total; + } + + // Construct list of worst spammers by Pid + static const unsigned char num_spammers = 10; + bool header = false; + + log_id_for_each(i) { + if (!(logMask & (1 << i))) { + continue; + } + + PidStatisticsCollection pids; + pids.clear(); + + LidStatistics &l = id(i); + UidStatisticsCollection::iterator iu; + for (iu = l.begin(); iu != l.end(); ++iu) { + UidStatistics &u = *(*iu); + PidStatisticsCollection::iterator ip; + for (ip = u.begin(); ip != u.end(); ++ip) { + PidStatistics *p = (*ip); + if (p->getPid() == p->gone) { + break; + } + + size_t mySizes = p->sizes(); + + PidStatisticsCollection::iterator q; + unsigned char num = 0; + for (q = pids.begin(); q != pids.end(); ++q) { + if (mySizes > (*q)->sizes()) { + pids.insert(q, p); + break; + } + // do we need to traverse deeper in the list? + if (++num > num_spammers) { + break; + } + } + if (q == pids.end()) { + pids.push_back(p); + } + } + } + + size_t threshold = sizes(i); + if (threshold < 65536) { + threshold = 65536; + } + threshold /= 100; + + PidStatisticsCollection::iterator pt = pids.begin(); + + for(int line = 0; + (pt != pids.end()) && (line < num_spammers); + ++line, pt = pids.erase(pt)) { + PidStatistics *p = *pt; + + size_t sizes = p->sizes(); + if (sizes < threshold) { + break; + } + + char *name = p->getName(); + pid_t pid = p->getPid(); + if (!name || !*name) { + name = pidToName(pid); + if (name) { + if (*name) { + p->setName(name); + } else { + free(name); + name = NULL; + } + } + } + + if (!header) { + string.appendFormat("\n\nChattiest clients:\n" + "log id %-*s PID[?] name", + spaces_total, "size/total"); + header = true; + } + + size_t sizesTotal = p->sizesTotal(); + + android::String8 sz(""); + sz.appendFormat((sizes != sizesTotal) ? "%zu/%zu" : "%zu", + sizes, sizesTotal); + + android::String8 pd(""); + pd.appendFormat("%u%c", pid, p->pidGone() ? '?' : ' '); + + string.appendFormat("\n%-7s%-*s %-7s%s", + line ? "" : android_log_id_to_name(i), + spaces_total, sz.string(), pd.string(), + name ? name : ""); + } + + pids.clear(); + } + + if (dgram_qlen_statistics) { + const unsigned short spaces_time = 6; + const unsigned long long max_seconds = 100000; + spaces = 0; + string.append("\n\nMinimum time between log events per dgram_qlen:\n"); + for(unsigned short i = 0; dgram_qlen(i); ++i) { + oldLength = string.length(); + if (spaces < 0) { + spaces = 0; + } + string.appendFormat("%*s%u", spaces, "", dgram_qlen(i)); + spaces += spaces_time + oldLength - string.length(); + } + string.append("\n"); + spaces = 0; + unsigned short n; + for(unsigned short i = 0; (n = dgram_qlen(i)); ++i) { + unsigned long long duration = minimum(i); + if (duration) { + duration /= n; + if (duration >= (NS_PER_SEC * max_seconds)) { + duration = NS_PER_SEC * (max_seconds - 1); + } + oldLength = string.length(); + if (spaces < 0) { + spaces = 0; + } + string.appendFormat("%*s", spaces, ""); + if (duration >= (NS_PER_SEC * 10)) { + string.appendFormat("%llu", + (duration + (NS_PER_SEC / 2)) + / NS_PER_SEC); + } else if (duration >= (NS_PER_SEC / (1000 / 10))) { + string.appendFormat("%llum", + (duration + (NS_PER_SEC / 2 / 1000)) + / (NS_PER_SEC / 1000)); + } else if (duration >= (NS_PER_SEC / (1000000 / 10))) { + string.appendFormat("%lluu", + (duration + (NS_PER_SEC / 2 / 1000000)) + / (NS_PER_SEC / 1000000)); + } else { + string.appendFormat("%llun", duration); + } + spaces -= string.length() - oldLength; + } + spaces += spaces_time; + } + } + + log_id_for_each(i) { + if (!(logMask & (1 << i))) { + continue; + } + + header = false; + bool first = true; + + UidStatisticsCollection::iterator ut; + for(ut = id(i).begin(); ut != id(i).end(); ++ut) { + UidStatistics *up = *ut; + if ((uid != AID_ROOT) && (uid != up->getUid())) { + continue; + } + + PidStatisticsCollection::iterator pt = up->begin(); + if (pt == up->end()) { + continue; + } + + android::String8 intermediate; + + if (!header) { + // header below tuned to match spaces_total and spaces_current + spaces = 0; + intermediate = string.format("%s: UID/PID Total size/num", + android_log_id_to_name(i)); + string.appendFormat("\n\n%-31sNow " + "UID/PID[?] Total Now", + intermediate.string()); + intermediate.clear(); + header = true; + } + + bool oneline = ++pt == up->end(); + --pt; + + if (!oneline) { + first = true; + } else if (!first && (spaces > 0)) { + string.appendFormat("%*s", spaces, ""); + } + spaces = 0; + + uid_t u = up->getUid(); + PidStatistics *pp = *pt; + pid_t p = pp->getPid(); + + intermediate = string.format(oneline + ? ((p == PidStatistics::gone) + ? "%d/?" + : "%d/%d%c") + : "%d", + u, p, pp->pidGone() ? '?' : '\0'); + string.appendFormat(first ? "\n%-12s" : "%-12s", + intermediate.string()); + intermediate.clear(); + + size_t elsTotal = up->elementsTotal(); + oldLength = string.length(); + string.appendFormat("%zu/%zu", up->sizesTotal(), elsTotal); + spaces += spaces_total + oldLength - string.length(); + + size_t els = up->elements(); + if (els == elsTotal) { + if (spaces < 0) { + spaces = 0; + } + string.appendFormat("%*s=", spaces, ""); + spaces = -1; + } else if (els) { + oldLength = string.length(); + if (spaces < 0) { + spaces = 0; + } + string.appendFormat("%*s%zu/%zu", spaces, "", up->sizes(), els); + spaces -= string.length() - oldLength; + } + spaces += spaces_current; + + first = !first; + + if (oneline) { + continue; + } + + size_t gone_szs = 0; + size_t gone_els = 0; + + for(; pt != up->end(); ++pt) { + pp = *pt; + p = pp->getPid(); + + // If a PID no longer has any current logs, and is not + // active anymore, skip & report totals for gone. + elsTotal = pp->elementsTotal(); + size_t szsTotal = pp->sizesTotal(); + if (p == pp->gone) { + gone_szs += szsTotal; + gone_els += elsTotal; + continue; + } + els = pp->elements(); + bool gone = pp->pidGone(); + if (gone && (els == 0)) { + // ToDo: garbage collection: move this statistical bucket + // from its current UID/PID to UID/? (races and + // wrap around are our achilles heel). Below is + // merely lipservice to catch PIDs that were still + // around when the stats were pruned to zero. + gone_szs += szsTotal; + gone_els += elsTotal; + continue; + } + + if (!first && (spaces > 0)) { + string.appendFormat("%*s", spaces, ""); + } + spaces = 0; + + intermediate = string.format(gone ? "%d/%d?" : "%d/%d", u, p); + string.appendFormat(first ? "\n%-12s" : "%-12s", + intermediate.string()); + intermediate.clear(); + + oldLength = string.length(); + string.appendFormat("%zu/%zu", szsTotal, elsTotal); + spaces += spaces_total + oldLength - string.length(); + + if (els == elsTotal) { + if (spaces < 0) { + spaces = 0; + } + string.appendFormat("%*s=", spaces, ""); + spaces = -1; + } else if (els) { + oldLength = string.length(); + if (spaces < 0) { + spaces = 0; + } + string.appendFormat("%*s%zu/%zu", spaces, "", + pp->sizes(), els); + spaces -= string.length() - oldLength; + } + spaces += spaces_current; + + first = !first; + } + + if (gone_els) { + if (!first && (spaces > 0)) { + string.appendFormat("%*s", spaces, ""); + } + + intermediate = string.format("%d/?", u); + string.appendFormat(first ? "\n%-12s" : "%-12s", + intermediate.string()); + intermediate.clear(); + + spaces = spaces_total + spaces_current; + + oldLength = string.length(); + string.appendFormat("%zu/%zu", gone_szs, gone_els); + spaces -= string.length() - oldLength; + + first = !first; + } + } + } + + *buf = strdup(string.string()); +} + +uid_t LogStatistics::pidToUid(pid_t pid) { + log_id_for_each(i) { + LidStatistics &l = id(i); + UidStatisticsCollection::iterator iu; + for (iu = l.begin(); iu != l.end(); ++iu) { + UidStatistics &u = *(*iu); + PidStatisticsCollection::iterator ip; + for (ip = u.begin(); ip != u.end(); ++ip) { + if ((*ip)->getPid() == pid) { + return u.getUid(); + } + } + } + } + return getuid(); // associate this with the logger +} diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h new file mode 100644 index 0000000..3733137 --- /dev/null +++ b/logd/LogStatistics.h @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2014 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. + */ + +#ifndef _LOGD_LOG_STATISTICS_H__ +#define _LOGD_LOG_STATISTICS_H__ + +#include <sys/types.h> + +#include <log/log.h> +#include <log/log_read.h> +#include <utils/List.h> + +#define log_id_for_each(i) \ + for (log_id_t i = LOG_ID_MIN; i < LOG_ID_MAX; i = (log_id_t) (i + 1)) + +class PidStatistics { + const pid_t pid; + + // Total + size_t mSizesTotal; + size_t mElementsTotal; + // Current + size_t mSizes; + size_t mElements; + + char *name; + bool mGone; + +public: + static const pid_t gone = (pid_t) -1; + + PidStatistics(pid_t pid, char *name = NULL); + PidStatistics(const PidStatistics ©); + ~PidStatistics(); + + pid_t getPid() const { return pid; } + bool pidGone(); + char *getName() const { return name; } + void setName(char *name); + + void add(unsigned short size); + bool subtract(unsigned short size); // returns true if stats and PID gone + void addTotal(size_t size, size_t element); + + size_t sizes() const { return mSizes; } + size_t elements() const { return mElements; } + + size_t sizesTotal() const { return mSizesTotal; } + size_t elementsTotal() const { return mElementsTotal; } + + // helper + static char *pidToName(pid_t pid); +}; + +typedef android::List<PidStatistics *> PidStatisticsCollection; + +class UidStatistics { + const uid_t uid; + + PidStatisticsCollection Pids; + + size_t mSizes; + size_t mElements; + +public: + UidStatistics(uid_t uid); + ~UidStatistics(); + + PidStatisticsCollection::iterator begin() { return Pids.begin(); } + PidStatisticsCollection::iterator end() { return Pids.end(); } + + uid_t getUid() { return uid; } + + void add(unsigned short size, pid_t pid); + void subtract(unsigned short size, pid_t pid); + void sort(); + + static const pid_t pid_all = (pid_t) -1; + + // fast track current value + size_t sizes() const { return mSizes; }; + size_t elements() const { return mElements; }; + + // statistical track + size_t sizes(pid_t pid); + size_t elements(pid_t pid); + + size_t sizesTotal(pid_t pid = pid_all); + size_t elementsTotal(pid_t pid = pid_all); + + // helper + static char *pidToName(pid_t pid) { return PidStatistics::pidToName(pid); } +}; + +typedef android::List<UidStatistics *> UidStatisticsCollection; + +class LidStatistics { + UidStatisticsCollection Uids; + +public: + LidStatistics(); + ~LidStatistics(); + + UidStatisticsCollection::iterator begin() { return Uids.begin(); } + UidStatisticsCollection::iterator end() { return Uids.end(); } + + void add(unsigned short size, uid_t uid, pid_t pid); + void subtract(unsigned short size, uid_t uid, pid_t pid); + void sort(); + + static const pid_t pid_all = (pid_t) -1; + static const uid_t uid_all = (uid_t) -1; + + size_t sizes(uid_t uid = uid_all, pid_t pid = pid_all); + size_t elements(uid_t uid = uid_all, pid_t pid = pid_all); + + size_t sizesTotal(uid_t uid = uid_all, pid_t pid = pid_all); + size_t elementsTotal(uid_t uid = uid_all, pid_t pid = pid_all); +}; + +// Log Statistics +class LogStatistics { + LidStatistics LogIds[LOG_ID_MAX]; + + size_t mSizes[LOG_ID_MAX]; + size_t mElements[LOG_ID_MAX]; + + bool dgram_qlen_statistics; + + static const unsigned short mBuckets[14]; + log_time mMinimum[sizeof(mBuckets) / sizeof(mBuckets[0])]; + +public: + const log_time start; + + LogStatistics(); + + LidStatistics &id(log_id_t log_id) { return LogIds[log_id]; } + + void enableDgramQlenStatistics() { dgram_qlen_statistics = true; } + static unsigned short dgram_qlen(unsigned short bucket); + unsigned long long minimum(unsigned short bucket); + void recordDiff(log_time diff, unsigned short bucket); + + void add(unsigned short size, log_id_t log_id, uid_t uid, pid_t pid); + void subtract(unsigned short size, log_id_t log_id, uid_t uid, pid_t pid); + void sort(); + + // fast track current value by id only + size_t sizes(log_id_t id) const { return mSizes[id]; } + size_t elements(log_id_t id) const { return mElements[id]; } + + // statistical track + static const log_id_t log_id_all = (log_id_t) -1; + static const uid_t uid_all = (uid_t) -1; + static const pid_t pid_all = (pid_t) -1; + + size_t sizes(log_id_t id, uid_t uid, pid_t pid = pid_all); + size_t elements(log_id_t id, uid_t uid, pid_t pid = pid_all); + size_t sizes() { return sizes(log_id_all, uid_all); } + size_t elements() { return elements(log_id_all, uid_all); } + + size_t sizesTotal(log_id_t id = log_id_all, + uid_t uid = uid_all, + pid_t pid = pid_all); + size_t elementsTotal(log_id_t id = log_id_all, + uid_t uid = uid_all, + pid_t pid = pid_all); + + // *strp = malloc, balance with free + void format(char **strp, uid_t uid, unsigned int logMask, log_time oldest); + + // helper + static char *pidToName(pid_t pid) { return PidStatistics::pidToName(pid); } + uid_t pidToUid(pid_t pid); +}; + +#endif // _LOGD_LOG_STATISTICS_H__ diff --git a/logd/LogTimes.cpp b/logd/LogTimes.cpp new file mode 100644 index 0000000..e7e3ec2 --- /dev/null +++ b/logd/LogTimes.cpp @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2014 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 <sys/prctl.h> + +#include "FlushCommand.h" +#include "LogBuffer.h" +#include "LogTimes.h" +#include "LogReader.h" + +pthread_mutex_t LogTimeEntry::timesLock = PTHREAD_MUTEX_INITIALIZER; + +const struct timespec LogTimeEntry::EPOCH = { 0, 1 }; + +LogTimeEntry::LogTimeEntry(LogReader &reader, SocketClient *client, + bool nonBlock, unsigned long tail, + unsigned int logMask, pid_t pid, + log_time start) + : mRefCount(1) + , mRelease(false) + , mError(false) + , threadRunning(false) + , threadTriggered(true) + , mReader(reader) + , mLogMask(logMask) + , mPid(pid) + , skipAhead(0) + , mCount(0) + , mTail(tail) + , mIndex(0) + , mClient(client) + , mStart(start) + , mNonBlock(nonBlock) + , mEnd(CLOCK_MONOTONIC) +{ } + +void LogTimeEntry::startReader_Locked(void) { + pthread_attr_t attr; + + threadRunning = true; + + if (!pthread_attr_init(&attr)) { + if (!pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) { + if (!pthread_create(&mThread, &attr, + LogTimeEntry::threadStart, this)) { + pthread_attr_destroy(&attr); + return; + } + } + pthread_attr_destroy(&attr); + } + threadRunning = false; + if (mClient) { + mClient->decRef(); + } + decRef_Locked(); +} + +void LogTimeEntry::threadStop(void *obj) { + LogTimeEntry *me = reinterpret_cast<LogTimeEntry *>(obj); + + lock(); + + me->threadRunning = false; + if (me->mNonBlock) { + me->error_Locked(); + } + + SocketClient *client = me->mClient; + + if (me->isError_Locked()) { + LogReader &reader = me->mReader; + LastLogTimes × = reader.logbuf().mTimes; + + LastLogTimes::iterator it = times.begin(); + while(it != times.end()) { + if (*it == me) { + times.erase(it); + me->release_Locked(); + break; + } + it++; + } + + me->mClient = NULL; + reader.release(client); + } + + if (client) { + client->decRef(); + } + + me->decRef_Locked(); + + unlock(); +} + +void *LogTimeEntry::threadStart(void *obj) { + prctl(PR_SET_NAME, "logd.reader.per"); + + LogTimeEntry *me = reinterpret_cast<LogTimeEntry *>(obj); + + pthread_cleanup_push(threadStop, obj); + + SocketClient *client = me->mClient; + if (!client) { + me->error(); + pthread_exit(NULL); + } + + LogBuffer &logbuf = me->mReader.logbuf(); + + bool privileged = FlushCommand::hasReadLogs(client); + + lock(); + + me->threadTriggered = true; + + while(me->threadTriggered && !me->isError_Locked()) { + + me->threadTriggered = false; + + log_time start = me->mStart; + + unlock(); + + if (me->mTail) { + logbuf.flushTo(client, start, privileged, FilterFirstPass, me); + } + start = logbuf.flushTo(client, start, privileged, FilterSecondPass, me); + + if (start == LogBufferElement::FLUSH_ERROR) { + me->error(); + } + + if (me->mNonBlock) { + lock(); + break; + } + + sched_yield(); + + lock(); + } + + unlock(); + + pthread_exit(NULL); + + pthread_cleanup_pop(true); + + return NULL; +} + +// A first pass to count the number of elements +bool LogTimeEntry::FilterFirstPass(const LogBufferElement *element, void *obj) { + LogTimeEntry *me = reinterpret_cast<LogTimeEntry *>(obj); + + LogTimeEntry::lock(); + + if (me->mCount == 0) { + me->mStart = element->getMonotonicTime(); + } + + if ((!me->mPid || (me->mPid == element->getPid())) + && (me->mLogMask & (1 << element->getLogId()))) { + ++me->mCount; + } + + LogTimeEntry::unlock(); + + return false; +} + +// A second pass to send the selected elements +bool LogTimeEntry::FilterSecondPass(const LogBufferElement *element, void *obj) { + LogTimeEntry *me = reinterpret_cast<LogTimeEntry *>(obj); + + LogTimeEntry::lock(); + + if (me->skipAhead) { + me->skipAhead--; + goto skip; + } + + me->mStart = element->getMonotonicTime(); + + // Truncate to close race between first and second pass + if (me->mNonBlock && me->mTail && (me->mIndex >= me->mCount)) { + goto skip; + } + + if ((me->mLogMask & (1 << element->getLogId())) == 0) { + goto skip; + } + + if (me->mPid && (me->mPid != element->getPid())) { + goto skip; + } + + if (me->isError_Locked()) { + goto skip; + } + + if (!me->mTail) { + goto ok; + } + + ++me->mIndex; + + if ((me->mCount > me->mTail) && (me->mIndex <= (me->mCount - me->mTail))) { + goto skip; + } + + if (!me->mNonBlock) { + me->mTail = 0; + } + +ok: + if (!me->skipAhead) { + LogTimeEntry::unlock(); + return true; + } + // FALLTHRU + +skip: + LogTimeEntry::unlock(); + return false; +} diff --git a/logd/LogTimes.h b/logd/LogTimes.h new file mode 100644 index 0000000..beaf646 --- /dev/null +++ b/logd/LogTimes.h @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2012-2013 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. + */ + +#ifndef _LOGD_LOG_TIMES_H__ +#define _LOGD_LOG_TIMES_H__ + +#include <pthread.h> +#include <time.h> +#include <sys/types.h> +#include <sysutils/SocketClient.h> +#include <utils/List.h> + +class LogReader; + +class LogTimeEntry { + static pthread_mutex_t timesLock; + unsigned int mRefCount; + bool mRelease; + bool mError; + bool threadRunning; + bool threadTriggered; + pthread_t mThread; + LogReader &mReader; + static void *threadStart(void *me); + static void threadStop(void *me); + const unsigned int mLogMask; + const pid_t mPid; + unsigned int skipAhead; + unsigned long mCount; + unsigned long mTail; + unsigned long mIndex; + +public: + LogTimeEntry(LogReader &reader, SocketClient *client, bool nonBlock, + unsigned long tail, unsigned int logMask, pid_t pid, + log_time start); + + SocketClient *mClient; + static const struct timespec EPOCH; + log_time mStart; + const bool mNonBlock; + const log_time mEnd; // only relevant if mNonBlock + + // Protect List manipulations + static void lock(void) { pthread_mutex_lock(×Lock); } + static void unlock(void) { pthread_mutex_unlock(×Lock); } + + void startReader_Locked(void); + + bool runningReader_Locked(void) const { + return threadRunning || mRelease || mError || mNonBlock; + } + void triggerReader_Locked(void) { threadTriggered = true; } + void triggerSkip_Locked(unsigned int skip) { skipAhead = skip; } + + // Called after LogTimeEntry removed from list, lock implicitly held + void release_Locked(void) { + mRelease = true; + if (mRefCount || threadRunning) { + return; + } + // No one else is holding a reference to this + delete this; + } + + // Called to mark socket in jeopardy + void error_Locked(void) { mError = true; } + void error(void) { lock(); mError = true; unlock(); } + + bool isError_Locked(void) const { return mRelease || mError; } + + // Mark Used + // Locking implied, grabbed when protection around loop iteration + void incRef_Locked(void) { ++mRefCount; } + + bool owned_Locked(void) const { return mRefCount != 0; } + + void decRef_Locked(void) { + if ((mRefCount && --mRefCount) || !mRelease || threadRunning) { + return; + } + // No one else is holding a reference to this + delete this; + } + + // flushTo filter callbacks + static bool FilterFirstPass(const LogBufferElement *element, void *me); + static bool FilterSecondPass(const LogBufferElement *element, void *me); +}; + +typedef android::List<LogTimeEntry *> LastLogTimes; + +#endif diff --git a/logd/LogWhiteBlackList.cpp b/logd/LogWhiteBlackList.cpp new file mode 100644 index 0000000..e87b604 --- /dev/null +++ b/logd/LogWhiteBlackList.cpp @@ -0,0 +1,239 @@ +/* + * Copyright (C) 2014 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 <ctype.h> + +#include <utils/String8.h> + +#include "LogWhiteBlackList.h" + +// White and Black list + +Prune::Prune(uid_t uid, pid_t pid) + : mUid(uid) + , mPid(pid) +{ } + +int Prune::cmp(uid_t uid, pid_t pid) const { + if ((mUid == uid_all) || (mUid == uid)) { + if (mPid == pid_all) { + return 0; + } + return pid - mPid; + } + return uid - mUid; +} + +void Prune::format(char **strp) { + if (mUid != uid_all) { + asprintf(strp, (mPid != pid_all) ? "%u/%u" : "%u", mUid, mPid); + } else { + // NB: mPid == pid_all can not happen if mUid == uid_all + asprintf(strp, (mPid != pid_all) ? "/%u" : "/", mPid); + } +} + +PruneList::PruneList() + : mWorstUidEnabled(false) { + mNaughty.clear(); + mNice.clear(); +} + +PruneList::~PruneList() { + PruneCollection::iterator it; + for (it = mNice.begin(); it != mNice.end();) { + delete (*it); + it = mNice.erase(it); + } + for (it = mNaughty.begin(); it != mNaughty.end();) { + delete (*it); + it = mNaughty.erase(it); + } +} + +int PruneList::init(char *str) { + mWorstUidEnabled = false; + PruneCollection::iterator it; + for (it = mNice.begin(); it != mNice.end();) { + delete (*it); + it = mNice.erase(it); + } + for (it = mNaughty.begin(); it != mNaughty.end();) { + delete (*it); + it = mNaughty.erase(it); + } + + if (!str) { + return 0; + } + + mWorstUidEnabled = false; + + for(; *str; ++str) { + if (isspace(*str)) { + continue; + } + + PruneCollection *list; + if ((*str == '~') || (*str == '!')) { // ~ supported, ! undocumented + ++str; + // special case, translates to worst UID at priority in blacklist + if (*str == '!') { + mWorstUidEnabled = true; + ++str; + if (!*str) { + break; + } + if (!isspace(*str)) { + return 1; + } + continue; + } + if (!*str) { + return 1; + } + list = &mNaughty; + } else { + list = &mNice; + } + + uid_t uid = Prune::uid_all; + if (isdigit(*str)) { + uid = 0; + do { + uid = uid * 10 + *str++ - '0'; + } while (isdigit(*str)); + } + + pid_t pid = Prune::pid_all; + if (*str == '/') { + ++str; + if (isdigit(*str)) { + pid = 0; + do { + pid = pid * 10 + *str++ - '0'; + } while (isdigit(*str)); + } + } + + if ((uid == Prune::uid_all) && (pid == Prune::pid_all)) { + return 1; + } + + if (*str && !isspace(*str)) { + return 1; + } + + // insert sequentially into list + PruneCollection::iterator it = list->begin(); + while (it != list->end()) { + Prune *p = *it; + int m = uid - p->mUid; + if (m == 0) { + if (p->mPid == p->pid_all) { + break; + } + if ((pid == p->pid_all) && (p->mPid != p->pid_all)) { + it = list->erase(it); + continue; + } + m = pid - p->mPid; + } + if (m <= 0) { + if (m < 0) { + list->insert(it, new Prune(uid,pid)); + } + break; + } + ++it; + } + if (it == list->end()) { + list->push_back(new Prune(uid,pid)); + } + if (!*str) { + break; + } + } + + return 0; +} + +void PruneList::format(char **strp) { + if (*strp) { + free(*strp); + *strp = NULL; + } + + static const char nice_format[] = " %s"; + const char *fmt = nice_format + 1; + + android::String8 string; + + if (mWorstUidEnabled) { + string.setTo("~!"); + fmt = nice_format; + } + + PruneCollection::iterator it; + + for (it = mNice.begin(); it != mNice.end(); ++it) { + char *a = NULL; + (*it)->format(&a); + + string.appendFormat(fmt, a); + fmt = nice_format; + + free(a); + } + + static const char naughty_format[] = " ~%s"; + fmt = naughty_format + (*fmt != ' '); + for (it = mNaughty.begin(); it != mNaughty.end(); ++it) { + char *a = NULL; + (*it)->format(&a); + + string.appendFormat(fmt, a); + fmt = naughty_format; + + free(a); + } + + *strp = strdup(string.string()); +} + +// ToDo: Lists are in sorted order, Prune->cmp() returns + or - +// If there is scaling issues, resort to a better algorithm than linear +// based on these assumptions. + +bool PruneList::naughty(LogBufferElement *element) { + PruneCollection::iterator it; + for (it = mNaughty.begin(); it != mNaughty.end(); ++it) { + if (!(*it)->cmp(element)) { + return true; + } + } + return false; +} + +bool PruneList::nice(LogBufferElement *element) { + PruneCollection::iterator it; + for (it = mNice.begin(); it != mNice.end(); ++it) { + if (!(*it)->cmp(element)) { + return true; + } + } + return false; +} diff --git a/logd/LogWhiteBlackList.h b/logd/LogWhiteBlackList.h new file mode 100644 index 0000000..769d651 --- /dev/null +++ b/logd/LogWhiteBlackList.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2014 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. + */ + +#ifndef _LOGD_LOG_WHITE_BLACK_LIST_H__ +#define _LOGD_LOG_WHITE_BLACK_LIST_H__ + +#include <sys/types.h> + +#include <utils/List.h> + +#include <LogBufferElement.h> + +// White and Blacklist + +class Prune { + friend class PruneList; + + const uid_t mUid; + const pid_t mPid; + int cmp(uid_t uid, pid_t pid) const; + +public: + static const uid_t uid_all = (uid_t) -1; + static const pid_t pid_all = (pid_t) -1; + + Prune(uid_t uid, pid_t pid); + + uid_t getUid() const { return mUid; } + pid_t getPid() const { return mPid; } + + int cmp(LogBufferElement *e) const { return cmp(e->getUid(), e->getPid()); } + + // *strp is malloc'd, use free to release + void format(char **strp); +}; + +typedef android::List<Prune *> PruneCollection; + +class PruneList { + PruneCollection mNaughty; + PruneCollection mNice; + bool mWorstUidEnabled; + +public: + PruneList(); + ~PruneList(); + + int init(char *str); + + bool naughty(LogBufferElement *element); + bool nice(LogBufferElement *element); + bool worstUidEnabled() const { return mWorstUidEnabled; } + + // *strp is malloc'd, use free to release + void format(char **strp); +}; + +#endif // _LOGD_LOG_WHITE_BLACK_LIST_H__ diff --git a/logd/README.auditd b/logd/README.auditd new file mode 100644 index 0000000..3f614a3 --- /dev/null +++ b/logd/README.auditd @@ -0,0 +1,17 @@ +Auditd Daemon + +The audit daemon is a simplified version of its desktop +counterpart designed to gather the audit logs from the +audit kernel subsystem. The audit subsystem of the kernel +includes Linux Security Modules (LSM) messages as well. + +To enable the audit subsystem, you must add this to your +kernel config: +CONFIG_AUDIT=y + +To enable a LSM, you must consult that LSM's documentation, the +example below is for SELinux: +CONFIG_SECURITY_SELINUX=y + +This does not include possible dependencies that may need to be +satisfied for that particular LSM. diff --git a/logd/README.property b/logd/README.property new file mode 100644 index 0000000..f4b3c3c --- /dev/null +++ b/logd/README.property @@ -0,0 +1,25 @@ +The properties that logd responds to are: + +name type default description +logd.auditd bool true Enable selinux audit daemon +logd.auditd.dmesg bool true selinux audit messages duplicated and + sent on to dmesg log +logd.statistics.dgram_qlen bool false Record dgram_qlen statistics. This + represents a performance impact and + is used to determine the platform's + minimum domain socket network FIFO + size (see source for details) based + on typical load (logcat -S to view) +persist.logd.size number 256K default size of the buffer for all + log ids at initial startup, at runtime + use: logcat -b all -G <value> +persist.logd.size.main number 256K Size of the buffer for the main log +persist.logd.size.system number 256K Size of the buffer for the system log +persist.logd.size.radio number 256K Size of the buffer for the radio log +persist.logd.size.event number 256K Size of the buffer for the event log +persist.logd.size.crash number 256K Size of the buffer for the crash log + +NB: +- number support multipliers (K or M) for convenience. Range is limited + to between 64K and 256M for log buffer sizes. Individual logs override the + global default. diff --git a/logd/event.logtags b/logd/event.logtags new file mode 100644 index 0000000..a63f034 --- /dev/null +++ b/logd/event.logtags @@ -0,0 +1,36 @@ +# The entries in this file map a sparse set of log tag numbers to tag names. +# This is installed on the device, in /system/etc, and parsed by logcat. +# +# Tag numbers are decimal integers, from 0 to 2^31. (Let's leave the +# negative values alone for now.) +# +# Tag names are one or more ASCII letters and numbers or underscores, i.e. +# "[A-Z][a-z][0-9]_". Do not include spaces or punctuation (the former +# impacts log readability, the latter makes regex searches more annoying). +# +# Tag numbers and names are separated by whitespace. Blank lines and lines +# starting with '#' are ignored. +# +# Optionally, after the tag names can be put a description for the value(s) +# of the tag. Description are in the format +# (<name>|data type[|data unit]) +# Multiple values are separated by commas. +# +# The data type is a number from the following values: +# 1: int +# 2: long +# 3: string +# 4: list +# +# The data unit is a number taken from the following list: +# 1: Number of objects +# 2: Number of bytes +# 3: Number of milliseconds +# 4: Number of allocations +# 5: Id +# 6: Percent +# Default value for data of type int/long is 2 (bytes). +# +# TODO: generate ".java" and ".h" files with integer constants from this file. + +1003 auditd (avc|3) diff --git a/logd/libaudit.c b/logd/libaudit.c new file mode 100644 index 0000000..ca88d1b --- /dev/null +++ b/logd/libaudit.c @@ -0,0 +1,276 @@ +/* + * Copyright 2012, Samsung Telecommunications of America + * Copyright (C) 2014 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. + * + * Written by William Roberts <w.roberts@sta.samsung.com> + * + */ + +#include <errno.h> +#include <string.h> +#include <unistd.h> + +#define LOG_TAG "libaudit" +#include <log/log.h> + +#include "libaudit.h" + +/** + * Waits for an ack from the kernel + * @param fd + * The netlink socket fd + * @param seq + * The current sequence number were acking on + * @return + * This function returns 0 on success, else -errno. + */ +static int get_ack(int fd, int16_t seq) +{ + int rc; + struct audit_message rep; + + /* Sanity check, this is an internal interface this shouldn't happen */ + if (fd < 0) { + return -EINVAL; + } + + rc = audit_get_reply(fd, &rep, GET_REPLY_BLOCKING, MSG_PEEK); + if (rc < 0) { + return rc; + } + + if (rep.nlh.nlmsg_type == NLMSG_ERROR) { + audit_get_reply(fd, &rep, GET_REPLY_BLOCKING, 0); + rc = ((struct nlmsgerr *)rep.data)->error; + if (rc) { + return -rc; + } + } + + if ((int16_t)rep.nlh.nlmsg_seq != seq) { + SLOGW("Expected sequence number between user space and kernel space is out of skew, " + "expected %u got %u", seq, rep.nlh.nlmsg_seq); + } + + return 0; +} + +/** + * + * @param fd + * The netlink socket fd + * @param type + * The type of netlink message + * @param data + * The data to send + * @param size + * The length of the data in bytes + * @return + * This function returns a positive sequence number on success, else -errno. + */ +static int audit_send(int fd, int type, const void *data, size_t size) +{ + int rc; + static int16_t sequence = 0; + struct audit_message req; + struct sockaddr_nl addr; + + memset(&req, 0, sizeof(req)); + memset(&addr, 0, sizeof(addr)); + + /* We always send netlink messaged */ + addr.nl_family = AF_NETLINK; + + /* Set up the netlink headers */ + req.nlh.nlmsg_type = type; + req.nlh.nlmsg_len = NLMSG_SPACE(size); + req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + + /* + * Check for a valid fd, even though sendto would catch this, its easier + * to always blindly increment the sequence number + */ + if (fd < 0) { + return -EBADF; + } + + /* Ensure the message is not too big */ + if (NLMSG_SPACE(size) > MAX_AUDIT_MESSAGE_LENGTH) { + SLOGE("netlink message is too large"); + return -EINVAL; + } + + /* Only memcpy in the data if it was specified */ + if (size && data) { + memcpy(NLMSG_DATA(&req.nlh), data, size); + } + + /* + * Only increment the sequence number on a guarantee + * you will send it to the kernel. + * + * Also, the sequence is defined as a u32 in the kernel + * struct. Using an int here might not work on 32/64 bit splits. A + * signed 64 bit value can overflow a u32..but a u32 + * might not fit in the response, so we need to use s32. + * Which is still kind of hackish since int could be 16 bits + * in size. The only safe type to use here is a signed 16 + * bit value. + */ + req.nlh.nlmsg_seq = ++sequence; + + /* While failing and its due to interrupts */ + + rc = TEMP_FAILURE_RETRY(sendto(fd, &req, req.nlh.nlmsg_len, 0, + (struct sockaddr*) &addr, sizeof(addr))); + + /* Not all the bytes were sent */ + if (rc < 0) { + rc = -errno; + SLOGE("Error sending data over the netlink socket: %s", strerror(-errno)); + goto out; + } else if ((uint32_t) rc != req.nlh.nlmsg_len) { + rc = -EPROTO; + goto out; + } + + /* We sent all the bytes, get the ack */ + rc = get_ack(fd, sequence); + + /* If the ack failed, return the error, else return the sequence number */ + rc = (rc == 0) ? (int) sequence : rc; + +out: + /* Don't let sequence roll to negative */ + if (sequence < 0) { + SLOGW("Auditd to Kernel sequence number has rolled over"); + sequence = 0; + } + + return rc; +} + +int audit_set_pid(int fd, uint32_t pid, rep_wait_t wmode) +{ + int rc; + struct audit_message rep; + struct audit_status status; + + memset(&status, 0, sizeof(status)); + + /* + * In order to set the auditd PID we send an audit message over the netlink + * socket with the pid field of the status struct set to our current pid, + * and the the mask set to AUDIT_STATUS_PID + */ + status.pid = pid; + status.mask = AUDIT_STATUS_PID; + + /* Let the kernel know this pid will be registering for audit events */ + rc = audit_send(fd, AUDIT_SET, &status, sizeof(status)); + if (rc < 0) { + SLOGE("Could net set pid for audit events, error: %s", strerror(-rc)); + return rc; + } + + /* + * In a request where we need to wait for a response, wait for the message + * and discard it. This message confirms and sync's us with the kernel. + * This daemon is now registered as the audit logger. Only wait if the + * wmode is != WAIT_NO + */ + if (wmode != WAIT_NO) { + /* TODO + * If the daemon dies and restarts the message didn't come back, + * so I went to non-blocking and it seemed to fix the bug. + * Need to investigate further. + */ + audit_get_reply(fd, &rep, GET_REPLY_NONBLOCKING, 0); + } + + return 0; +} + +int audit_open() +{ + return socket(PF_NETLINK, SOCK_RAW, NETLINK_AUDIT); +} + +int audit_get_reply(int fd, struct audit_message *rep, reply_t block, int peek) +{ + ssize_t len; + int flags; + int rc = 0; + + struct sockaddr_nl nladdr; + socklen_t nladdrlen = sizeof(nladdr); + + if (fd < 0) { + return -EBADF; + } + + /* Set up the flags for recv from */ + flags = (block == GET_REPLY_NONBLOCKING) ? MSG_DONTWAIT : 0; + flags |= peek; + + /* + * Get the data from the netlink socket but on error we need to be carefull, + * the interface shows that EINTR can never be returned, other errors, + * however, can be returned. + */ + len = TEMP_FAILURE_RETRY(recvfrom(fd, rep, sizeof(*rep), flags, + (struct sockaddr*) &nladdr, &nladdrlen)); + + /* + * EAGAIN should be re-tried until success or another error manifests. + */ + if (len < 0) { + rc = -errno; + if (block == GET_REPLY_NONBLOCKING && rc == -EAGAIN) { + /* If request is non blocking and errno is EAGAIN, just return 0 */ + return 0; + } + SLOGE("Error receiving from netlink socket, error: %s", strerror(-rc)); + return rc; + } + + if (nladdrlen != sizeof(nladdr)) { + SLOGE("Protocol fault, error: %s", strerror(EPROTO)); + return -EPROTO; + } + + /* Make sure the netlink message was not spoof'd */ + if (nladdr.nl_pid) { + SLOGE("Invalid netlink pid received, expected 0 got: %d", nladdr.nl_pid); + return -EINVAL; + } + + /* Check if the reply from the kernel was ok */ + if (!NLMSG_OK(&rep->nlh, (size_t)len)) { + rc = (len == sizeof(*rep)) ? -EFBIG : -EBADE; + SLOGE("Bad kernel response %s", strerror(-rc)); + } + + return rc; +} + +void audit_close(int fd) +{ + int rc = close(fd); + if (rc < 0) { + SLOGE("Attempting to close invalid fd %d, error: %s", fd, strerror(errno)); + } + return; +} diff --git a/logd/libaudit.h b/logd/libaudit.h new file mode 100644 index 0000000..cb114f9 --- /dev/null +++ b/logd/libaudit.h @@ -0,0 +1,104 @@ +/* + * Copyright 2012, Samsung Telecommunications of America + * Copyright (C) 2014 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. + * + * Written by William Roberts <w.roberts@sta.samsung.com> + */ + +#ifndef _LIBAUDIT_H_ +#define _LIBAUDIT_H_ + +#include <stdint.h> +#include <sys/cdefs.h> +#include <sys/socket.h> +#include <sys/types.h> + +#include <linux/netlink.h> +#include <linux/audit.h> + +__BEGIN_DECLS + +#define MAX_AUDIT_MESSAGE_LENGTH 8970 + +typedef enum { + GET_REPLY_BLOCKING=0, + GET_REPLY_NONBLOCKING +} reply_t; + +typedef enum { + WAIT_NO, + WAIT_YES +} rep_wait_t; + +/* type == AUDIT_SIGNAL_INFO */ +struct audit_sig_info { + uid_t uid; + pid_t pid; + char ctx[0]; +}; + +struct audit_message { + struct nlmsghdr nlh; + char data[MAX_AUDIT_MESSAGE_LENGTH]; +}; + +/** + * Opens a connection to the Audit netlink socket + * @return + * A valid fd on success or < 0 on error with errno set. + * Returns the same errors as man 2 socket. + */ +extern int audit_open(void); + +/** + * Closes the fd returned from audit_open() + * @param fd + * The fd to close + */ +extern void audit_close(int fd); + +/** + * + * @param fd + * The fd returned by a call to audit_open() + * @param rep + * The response struct to store the response in. + * @param block + * Whether or not to block on IO + * @param peek + * Whether or not we are to remove the message from + * the queue when we do a read on the netlink socket. + * @return + * This function returns 0 on success, else -errno. + */ +extern int audit_get_reply(int fd, struct audit_message *rep, reply_t block, + int peek); + +/** + * Sets a pid to recieve audit netlink events from the kernel + * @param fd + * The fd returned by a call to audit_open() + * @param pid + * The pid whom to set as the reciever of audit messages + * @param wmode + * Whether or not to block on the underlying socket io calls. + * @return + * This function returns 0 on success, -errno on error. + */ +extern int audit_set_pid(int fd, uint32_t pid, rep_wait_t wmode); + +__END_DECLS + +#endif diff --git a/logd/main.cpp b/logd/main.cpp new file mode 100644 index 0000000..ece5a3a --- /dev/null +++ b/logd/main.cpp @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2012-2013 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 <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <sched.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/capability.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include <linux/prctl.h> + +#include <cutils/properties.h> + +#include "private/android_filesystem_config.h" +#include "CommandListener.h" +#include "LogBuffer.h" +#include "LogListener.h" +#include "LogAudit.h" + +// +// The service is designed to be run by init, it does not respond well +// to starting up manually. When starting up manually the sockets will +// fail to open typically for one of the following reasons: +// EADDRINUSE if logger is running. +// EACCESS if started without precautions (below) +// +// Here is a cookbook procedure for starting up logd manually assuming +// init is out of the way, pedantically all permissions and selinux +// security is put back in place: +// +// setenforce 0 +// rm /dev/socket/logd* +// chmod 777 /dev/socket +// # here is where you would attach the debugger or valgrind for example +// runcon u:r:logd:s0 /system/bin/logd </dev/null >/dev/null 2>&1 & +// sleep 1 +// chmod 755 /dev/socket +// chown logd.logd /dev/socket/logd* +// restorecon /dev/socket/logd* +// setenforce 1 +// +// If minimalism prevails, typical for debugging and security is not a concern: +// +// setenforce 0 +// chmod 777 /dev/socket +// logd +// + +static int drop_privs() { + struct sched_param param; + memset(¶m, 0, sizeof(param)); + + if (sched_setscheduler((pid_t) 0, SCHED_BATCH, ¶m) < 0) { + return -1; + } + + if (prctl(PR_SET_KEEPCAPS, 1) < 0) { + return -1; + } + + if (setgid(AID_LOGD) != 0) { + return -1; + } + + if (setuid(AID_LOGD) != 0) { + return -1; + } + + struct __user_cap_header_struct capheader; + struct __user_cap_data_struct capdata[2]; + memset(&capheader, 0, sizeof(capheader)); + memset(&capdata, 0, sizeof(capdata)); + capheader.version = _LINUX_CAPABILITY_VERSION_3; + capheader.pid = 0; + + capdata[CAP_TO_INDEX(CAP_SYSLOG)].permitted = CAP_TO_MASK(CAP_SYSLOG); + capdata[CAP_TO_INDEX(CAP_AUDIT_CONTROL)].permitted |= CAP_TO_MASK(CAP_AUDIT_CONTROL); + + capdata[0].effective = capdata[0].permitted; + capdata[1].effective = capdata[1].permitted; + capdata[0].inheritable = 0; + capdata[1].inheritable = 0; + + if (capset(&capheader, &capdata[0]) < 0) { + return -1; + } + + return 0; +} + +// Property helper +static bool property_get_bool(const char *key, bool def) { + char property[PROPERTY_VALUE_MAX]; + property_get(key, property, ""); + + if (!strcasecmp(property, "true")) { + return true; + } + if (!strcasecmp(property, "false")) { + return false; + } + + return def; +} + +// Foreground waits for exit of the three main persistent threads that +// are started here. The three threads are created to manage UNIX +// domain client sockets for writing, reading and controlling the user +// space logger. Additional transitory per-client threads are created +// for each reader once they register. +int main() { + bool auditd = property_get_bool("logd.auditd", true); + + int fdDmesg = -1; + if (auditd && property_get_bool("logd.auditd.dmesg", true)) { + fdDmesg = open("/dev/kmsg", O_WRONLY); + } + + if (drop_privs() != 0) { + return -1; + } + + // Serves the purpose of managing the last logs times read on a + // socket connection, and as a reader lock on a range of log + // entries. + + LastLogTimes *times = new LastLogTimes(); + + // LogBuffer is the object which is responsible for holding all + // log entries. + + LogBuffer *logBuf = new LogBuffer(times); + + if (property_get_bool("logd.statistics.dgram_qlen", false)) { + logBuf->enableDgramQlenStatistics(); + } + + // LogReader listens on /dev/socket/logdr. When a client + // connects, log entries in the LogBuffer are written to the client. + + LogReader *reader = new LogReader(logBuf); + if (reader->startListener()) { + exit(1); + } + + // LogListener listens on /dev/socket/logdw for client + // initiated log messages. New log entries are added to LogBuffer + // and LogReader is notified to send updates to connected clients. + + LogListener *swl = new LogListener(logBuf, reader); + // Backlog and /proc/sys/net/unix/max_dgram_qlen set to large value + if (swl->startListener(300)) { + exit(1); + } + + // Command listener listens on /dev/socket/logd for incoming logd + // administrative commands. + + CommandListener *cl = new CommandListener(logBuf, reader, swl); + if (cl->startListener()) { + exit(1); + } + + // LogAudit listens on NETLINK_AUDIT socket for selinux + // initiated log messages. New log entries are added to LogBuffer + // and LogReader is notified to send updates to connected clients. + + if (auditd) { + // failure is an option ... messages are in dmesg (required by standard) + LogAudit *al = new LogAudit(logBuf, reader, fdDmesg); + if (al->startListener()) { + delete al; + close(fdDmesg); + } + } + + pause(); + exit(0); +} + diff --git a/logd/tests/Android.mk b/logd/tests/Android.mk new file mode 100644 index 0000000..f851288 --- /dev/null +++ b/logd/tests/Android.mk @@ -0,0 +1,53 @@ +# +# Copyright (C) 2014 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. +# + +LOCAL_PATH := $(call my-dir) + +# ----------------------------------------------------------------------------- +# Benchmarks. (see ../../liblog/tests) +# ----------------------------------------------------------------------------- + +test_module_prefix := logd- +test_tags := tests + +# ----------------------------------------------------------------------------- +# Unit tests. +# ----------------------------------------------------------------------------- + +test_c_flags := \ + -fstack-protector-all \ + -g \ + -Wall -Wextra \ + -Werror \ + -fno-builtin \ + +ifneq ($(TARGET_USES_LOGD),false) +test_c_flags += -DTARGET_USES_LOGD=1 +endif + +test_src_files := \ + logd_test.cpp + +# Build tests for the logger. Run with: +# adb shell /data/nativetest/logd-unit-tests/logd-unit-tests +include $(CLEAR_VARS) +LOCAL_MODULE := $(test_module_prefix)unit-tests +LOCAL_MODULE_TAGS := $(test_tags) +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk +LOCAL_CFLAGS += $(test_c_flags) +LOCAL_SHARED_LIBRARIES := libcutils +LOCAL_SRC_FILES := $(test_src_files) +include $(BUILD_NATIVE_TEST) diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp new file mode 100644 index 0000000..957fdb5 --- /dev/null +++ b/logd/tests/logd_test.cpp @@ -0,0 +1,703 @@ +/* + * Copyright (C) 2014 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 <fcntl.h> +#include <poll.h> +#include <signal.h> +#include <stdio.h> +#include <string.h> + +#include <gtest/gtest.h> + +#include "cutils/sockets.h" +#include "log/log.h" +#include "log/logger.h" + +#define __unused __attribute__((__unused__)) + +/* + * returns statistics + */ +static void my_android_logger_get_statistics(char *buf, size_t len) +{ + snprintf(buf, len, "getStatistics 0 1 2 3 4"); + int sock = socket_local_client("logd", + ANDROID_SOCKET_NAMESPACE_RESERVED, + SOCK_STREAM); + if (sock >= 0) { + if (write(sock, buf, strlen(buf) + 1) > 0) { + ssize_t ret; + while ((ret = read(sock, buf, len)) > 0) { + if ((size_t)ret == len) { + break; + } + len -= ret; + buf += ret; + + struct pollfd p = { + .fd = sock, + .events = POLLIN, + .revents = 0 + }; + + ret = poll(&p, 1, 20); + if ((ret <= 0) || !(p.revents & POLLIN)) { + break; + } + } + } + close(sock); + } +} + +static void alloc_statistics(char **buffer, size_t *length) +{ + size_t len = 8192; + char *buf; + + for(int retry = 32; (retry >= 0); delete [] buf, --retry) { + buf = new char [len]; + my_android_logger_get_statistics(buf, len); + + buf[len-1] = '\0'; + size_t ret = atol(buf) + 1; + if (ret < 4) { + delete [] buf; + buf = NULL; + break; + } + bool check = ret <= len; + len = ret; + if (check) { + break; + } + len += len / 8; // allow for some slop + } + *buffer = buf; + *length = len; +} + +static char *find_benchmark_spam(char *cp) +{ + // liblog_benchmarks has been run designed to SPAM. The signature of + // a noisiest UID statistics is one of the following: + // + // main: UID/PID Total size/num Now UID/PID[?] Total + // 0 7500306/304207 71608/3183 0/4225? 7454388/303656 + // <wrap> 93432/1012 + // -or- + // 0/gone 7454388/303656 93432/1012 + // + // basically if we see a *large* number of 0/????? entries + unsigned long value; + do { + char *benchmark = strstr(cp, " 0/"); + char *benchmark_newline = strstr(cp, "\n0/"); + if (!benchmark) { + benchmark = benchmark_newline; + } + if (benchmark_newline && (benchmark > benchmark_newline)) { + benchmark = benchmark_newline; + } + cp = benchmark; + if (!cp) { + break; + } + cp += 3; + while (isdigit(*cp) || (*cp == 'g') || (*cp == 'o') || (*cp == 'n')) { + ++cp; + } + value = 0; + // ###? or gone + if ((*cp == '?') || (*cp == 'e')) { + while (*++cp == ' '); + while (isdigit(*cp)) { + value = value * 10ULL + *cp - '0'; + ++cp; + } + if (*cp != '/') { + value = 0; + continue; + } + while (isdigit(*++cp)); + while (*cp == ' ') ++cp; + if (!isdigit(*cp)) { + value = 0; + } + } + } while ((value < 900000ULL) && *cp); + return cp; +} + +TEST(logd, statistics) { + size_t len; + char *buf; + + alloc_statistics(&buf, &len); + +#ifdef TARGET_USES_LOGD + ASSERT_TRUE(NULL != buf); +#else + if (!buf) { + return; + } +#endif + + // remove trailing FF + char *cp = buf + len - 1; + *cp = '\0'; + bool truncated = *--cp != '\f'; + if (!truncated) { + *cp = '\0'; + } + + // squash out the byte count + cp = buf; + if (!truncated) { + while (isdigit(*cp) || (*cp == '\n')) { + ++cp; + } + } + + fprintf(stderr, "%s", cp); + + EXPECT_LT((size_t)64, strlen(cp)); + + EXPECT_EQ(0, truncated); + +#ifdef TARGET_USES_LOGD + char *main_logs = strstr(cp, "\nmain:"); + EXPECT_TRUE(NULL != main_logs); + + char *radio_logs = strstr(cp, "\nradio:"); + EXPECT_TRUE(NULL != radio_logs); + + char *system_logs = strstr(cp, "\nsystem:"); + EXPECT_TRUE(NULL != system_logs); + + char *events_logs = strstr(cp, "\nevents:"); + EXPECT_TRUE(NULL != events_logs); +#endif + + // Parse timing stats + + cp = strstr(cp, "Minimum time between log events per dgram_qlen:"); + + if (cp) { + while (*cp && (*cp != '\n')) { + ++cp; + } + if (*cp == '\n') { + ++cp; + } + + char *list_of_spans = cp; + EXPECT_NE('\0', *list_of_spans); + + unsigned short number_of_buckets = 0; + unsigned short *dgram_qlen = NULL; + unsigned short bucket = 0; + while (*cp && (*cp != '\n')) { + bucket = 0; + while (isdigit(*cp)) { + bucket = bucket * 10 + *cp - '0'; + ++cp; + } + while (*cp == ' ') { + ++cp; + } + if (!bucket) { + break; + } + unsigned short *new_dgram_qlen = new unsigned short[number_of_buckets + 1]; + EXPECT_TRUE(new_dgram_qlen != NULL); + if (dgram_qlen) { + memcpy(new_dgram_qlen, dgram_qlen, sizeof(*dgram_qlen) * number_of_buckets); + delete [] dgram_qlen; + } + + dgram_qlen = new_dgram_qlen; + dgram_qlen[number_of_buckets++] = bucket; + } + + char *end_of_spans = cp; + EXPECT_NE('\0', *end_of_spans); + + EXPECT_LT(5, number_of_buckets); + + unsigned long long *times = new unsigned long long [number_of_buckets]; + ASSERT_TRUE(times != NULL); + + memset(times, 0, sizeof(*times) * number_of_buckets); + + while (*cp == '\n') { + ++cp; + } + + unsigned short number_of_values = 0; + unsigned long long value; + while (*cp && (*cp != '\n')) { + EXPECT_GE(number_of_buckets, number_of_values); + + value = 0; + while (isdigit(*cp)) { + value = value * 10ULL + *cp - '0'; + ++cp; + } + + switch(*cp) { + case ' ': + case '\n': + value *= 1000ULL; + /* FALLTHRU */ + case 'm': + value *= 1000ULL; + /* FALLTHRU */ + case 'u': + value *= 1000ULL; + /* FALLTHRU */ + case 'n': + default: + break; + } + while (*++cp == ' '); + + if (!value) { + break; + } + + times[number_of_values] = value; + ++number_of_values; + } + +#ifdef TARGET_USES_LOGD + EXPECT_EQ(number_of_values, number_of_buckets); +#endif + + FILE *fp; + ASSERT_TRUE(NULL != (fp = fopen("/proc/sys/net/unix/max_dgram_qlen", "r"))); + + unsigned max_dgram_qlen = 0; + fscanf(fp, "%u", &max_dgram_qlen); + + fclose(fp); + + // Find launch point + unsigned short launch = 0; + unsigned long long total = 0; + do { + total += times[launch]; + } while (((++launch < number_of_buckets) + && ((total / launch) >= (times[launch] / 8ULL))) + || (launch == 1)); // too soon + + bool failure = number_of_buckets <= launch; + if (!failure) { + unsigned short l = launch; + if (l >= number_of_buckets) { + l = number_of_buckets - 1; + } + failure = max_dgram_qlen < dgram_qlen[l]; + } + + // We can get failure if at any time liblog_benchmarks has been run + // because designed to overload /proc/sys/net/unix/max_dgram_qlen even + // at excessive values like 20000. It does so to measure the raw processing + // performance of logd. + if (failure) { + cp = find_benchmark_spam(cp); + } + + if (cp) { + // Fake a failure, but without the failure code + if (number_of_buckets <= launch) { + printf ("Expected: number_of_buckets > launch, actual: %u vs %u\n", + number_of_buckets, launch); + } + if (launch >= number_of_buckets) { + launch = number_of_buckets - 1; + } + if (max_dgram_qlen < dgram_qlen[launch]) { + printf ("Expected: max_dgram_qlen >= dgram_qlen[%d]," + " actual: %u vs %u\n", + launch, max_dgram_qlen, dgram_qlen[launch]); + } + } else +#ifndef TARGET_USES_LOGD + if (total) +#endif + { + EXPECT_GT(number_of_buckets, launch); + if (launch >= number_of_buckets) { + launch = number_of_buckets - 1; + } + EXPECT_GE(max_dgram_qlen, dgram_qlen[launch]); + } + + delete [] dgram_qlen; + delete [] times; + } + delete [] buf; +} + +static void caught_signal(int signum __unused) { } + +static void dump_log_msg(const char *prefix, + log_msg *msg, unsigned int version, int lid) { + switch(msg->entry.hdr_size) { + case 0: + version = 1; + break; + + case sizeof(msg->entry_v2): + if (version == 0) { + version = 2; + } + break; + } + + fprintf(stderr, "%s: v%u[%u] ", prefix, version, msg->len()); + if (version != 1) { + fprintf(stderr, "hdr_size=%u ", msg->entry.hdr_size); + } + fprintf(stderr, "pid=%u tid=%u %u.%09u ", + msg->entry.pid, msg->entry.tid, msg->entry.sec, msg->entry.nsec); + switch(version) { + case 1: + break; + case 2: + fprintf(stderr, "euid=%u ", msg->entry_v2.euid); + break; + case 3: + default: + lid = msg->entry.lid; + break; + } + + switch(lid) { + case 0: + fprintf(stderr, "lid=main "); + break; + case 1: + fprintf(stderr, "lid=radio "); + break; + case 2: + fprintf(stderr, "lid=events "); + break; + case 3: + fprintf(stderr, "lid=system "); + break; + default: + if (lid >= 0) { + fprintf(stderr, "lid=%d ", lid); + } + } + + unsigned int len = msg->entry.len; + fprintf(stderr, "msg[%u]={", len); + unsigned char *cp = reinterpret_cast<unsigned char *>(msg->msg()); + while(len) { + unsigned char *p = cp; + while (*p && (((' ' <= *p) && (*p < 0x7F)) || (*p == '\n'))) { + ++p; + } + if (((p - cp) > 3) && !*p && ((unsigned int)(p - cp) < len)) { + fprintf(stderr, "\""); + while (*cp) { + fprintf(stderr, (*cp != '\n') ? "%c" : "\\n", *cp); + ++cp; + --len; + } + fprintf(stderr, "\""); + } else { + fprintf(stderr, "%02x", *cp); + } + ++cp; + if (--len) { + fprintf(stderr, ", "); + } + } + fprintf(stderr, "}\n"); +} + +TEST(logd, both) { + log_msg msg; + + // check if we can read any logs from logd + bool user_logger_available = false; + bool user_logger_content = false; + + int fd = socket_local_client("logdr", + ANDROID_SOCKET_NAMESPACE_RESERVED, + SOCK_SEQPACKET); + if (fd >= 0) { + struct sigaction ignore, old_sigaction; + memset(&ignore, 0, sizeof(ignore)); + ignore.sa_handler = caught_signal; + sigemptyset(&ignore.sa_mask); + sigaction(SIGALRM, &ignore, &old_sigaction); + unsigned int old_alarm = alarm(10); + + static const char ask[] = "dumpAndClose lids=0,1,2,3"; + user_logger_available = write(fd, ask, sizeof(ask)) == sizeof(ask); + + user_logger_content = recv(fd, msg.buf, sizeof(msg), 0) > 0; + + if (user_logger_content) { + dump_log_msg("user", &msg, 3, -1); + } + + alarm(old_alarm); + sigaction(SIGALRM, &old_sigaction, NULL); + + close(fd); + } + + // check if we can read any logs from kernel logger + bool kernel_logger_available = false; + bool kernel_logger_content = false; + + static const char *loggers[] = { + "/dev/log/main", "/dev/log_main", + "/dev/log/radio", "/dev/log_radio", + "/dev/log/events", "/dev/log_events", + "/dev/log/system", "/dev/log_system", + }; + + for (unsigned int i = 0; i < (sizeof(loggers) / sizeof(loggers[0])); ++i) { + fd = open(loggers[i], O_RDONLY); + if (fd < 0) { + continue; + } + kernel_logger_available = true; + fcntl(fd, F_SETFL, O_RDONLY | O_NONBLOCK); + int result = TEMP_FAILURE_RETRY(read(fd, msg.buf, sizeof(msg))); + if (result > 0) { + kernel_logger_content = true; + dump_log_msg("kernel", &msg, 0, i / 2); + } + close(fd); + } + + static const char yes[] = "\xE2\x9C\x93"; + static const char no[] = "\xE2\x9c\x98"; + fprintf(stderr, + "LOGGER Available Content\n" + "user %-13s%s\n" + "kernel %-13s%s\n" + " status %-11s%s\n", + (user_logger_available) ? yes : no, + (user_logger_content) ? yes : no, + (kernel_logger_available) ? yes : no, + (kernel_logger_content) ? yes : no, + (user_logger_available && kernel_logger_available) ? "WARNING" : "ok", + (user_logger_content && kernel_logger_content) ? "ERROR" : "ok"); + + if (user_logger_available && kernel_logger_available) { + printf("WARNING: kernel & user logger; both consuming resources!!!\n"); + } + + EXPECT_EQ(0, user_logger_content && kernel_logger_content); + EXPECT_EQ(0, !user_logger_content && !kernel_logger_content); +} + +// BAD ROBOT +// Benchmark threshold are generally considered bad form unless there is +// is some human love applied to the continued maintenance and whether the +// thresholds are tuned on a per-target basis. Here we check if the values +// are more than double what is expected. Doubling will not prevent failure +// on busy or low-end systems that could have a tendency to stretch values. +// +// The primary goal of this test is to simulate a spammy app (benchmark +// being the worst) and check to make sure the logger can deal with it +// appropriately by checking all the statistics are in an expected range. +// +TEST(logd, benchmark) { + size_t len; + char *buf; + + alloc_statistics(&buf, &len); + bool benchmark_already_run = buf && find_benchmark_spam(buf); + delete [] buf; + + if (benchmark_already_run) { + fprintf(stderr, "WARNING: spam already present and too much history\n" + " false OK for prune by worst UID check\n"); + } + + FILE *fp; + + // Introduce some extreme spam for the worst UID filter + ASSERT_TRUE(NULL != (fp = popen( + "/data/nativetest/liblog-benchmarks/liblog-benchmarks", + "r"))); + + char buffer[5120]; + + static const char *benchmarks[] = { + "BM_log_maximum_retry ", + "BM_log_maximum ", + "BM_clock_overhead ", + "BM_log_overhead ", + "BM_log_latency ", + "BM_log_delay " + }; + static const unsigned int log_maximum_retry = 0; + static const unsigned int log_maximum = 1; + static const unsigned int clock_overhead = 2; + static const unsigned int log_overhead = 3; + static const unsigned int log_latency = 4; + static const unsigned int log_delay = 5; + + unsigned long ns[sizeof(benchmarks) / sizeof(benchmarks[0])]; + + memset(ns, 0, sizeof(ns)); + + while (fgets(buffer, sizeof(buffer), fp)) { + for (unsigned i = 0; i < sizeof(ns) / sizeof(ns[0]); ++i) { + char *cp = strstr(buffer, benchmarks[i]); + if (!cp) { + continue; + } + sscanf(cp, "%*s %lu %lu", &ns[i], &ns[i]); + fprintf(stderr, "%-22s%8lu\n", benchmarks[i], ns[i]); + } + } + int ret = pclose(fp); + + if (!WIFEXITED(ret) || (WEXITSTATUS(ret) == 127)) { + fprintf(stderr, + "WARNING: " + "/data/nativetest/liblog-benchmarks/liblog-benchmarks missing\n" + " can not perform test\n"); + return; + } + +#ifdef TARGET_USES_LOGD + EXPECT_GE(100000UL, ns[log_maximum_retry]); // 42777 user +#else + EXPECT_GE(10000UL, ns[log_maximum_retry]); // 5636 kernel +#endif + +#ifdef TARGET_USES_LOGD + EXPECT_GE(30000UL, ns[log_maximum]); // 27305 user +#else + EXPECT_GE(10000UL, ns[log_maximum]); // 5637 kernel +#endif + + EXPECT_GE(4096UL, ns[clock_overhead]); // 4095 + +#ifdef TARGET_USES_LOGD + EXPECT_GE(250000UL, ns[log_overhead]); // 121876 user +#else + EXPECT_GE(100000UL, ns[log_overhead]); // 50945 kernel +#endif + +#ifdef TARGET_USES_LOGD + EXPECT_GE(7500UL, ns[log_latency]); // 3718 user space +#else + EXPECT_GE(500000UL, ns[log_latency]); // 254200 kernel +#endif + +#ifdef TARGET_USES_LOGD + EXPECT_GE(20000000UL, ns[log_delay]); // 10500289 user +#else + EXPECT_GE(55000UL, ns[log_delay]); // 27341 kernel +#endif + + for (unsigned i = 0; i < sizeof(ns) / sizeof(ns[0]); ++i) { + EXPECT_NE(0UL, ns[i]); + } + + alloc_statistics(&buf, &len); + +#ifdef TARGET_USES_LOGD + bool collected_statistics = !!buf; + EXPECT_EQ(true, collected_statistics); +#else + if (!buf) { + return; + } +#endif + + ASSERT_TRUE(NULL != buf); + + char *benchmark_statistics_found = find_benchmark_spam(buf); + ASSERT_TRUE(benchmark_statistics_found != NULL); + + // Check how effective the SPAM filter is, parse out Now size. + // Total Now + // 0/4225? 7454388/303656 31488/755 + // ^-- benchmark_statistics_found + + unsigned long nowSpamSize = atol(benchmark_statistics_found); + + delete [] buf; + + ASSERT_NE(0UL, nowSpamSize); + + // Determine if we have the spam filter enabled + int sock = socket_local_client("logd", + ANDROID_SOCKET_NAMESPACE_RESERVED, + SOCK_STREAM); + + ASSERT_TRUE(sock >= 0); + + static const char getPruneList[] = "getPruneList"; + if (write(sock, getPruneList, sizeof(getPruneList)) > 0) { + char buffer[80]; + memset(buffer, 0, sizeof(buffer)); + read(sock, buffer, sizeof(buffer)); + char *cp = strchr(buffer, '\n'); + if (!cp || (cp[1] != '~') || (cp[2] != '!')) { + close(sock); + fprintf(stderr, + "WARNING: " + "Logger has SPAM filtration turned off \"%s\"\n", buffer); + return; + } + } else { + int save_errno = errno; + close(sock); + FAIL() << "Can not send " << getPruneList << " to logger -- " << strerror(save_errno); + } + + static const unsigned long expected_absolute_minimum_log_size = 65536UL; + unsigned long totalSize = expected_absolute_minimum_log_size; + static const char getSize[] = { + 'g', 'e', 't', 'L', 'o', 'g', 'S', 'i', 'z', 'e', ' ', + LOG_ID_MAIN + '0', '\0' + }; + if (write(sock, getSize, sizeof(getSize)) > 0) { + char buffer[80]; + memset(buffer, 0, sizeof(buffer)); + read(sock, buffer, sizeof(buffer)); + totalSize = atol(buffer); + if (totalSize < expected_absolute_minimum_log_size) { + fprintf(stderr, + "WARNING: " + "Logger had unexpected referenced size \"%s\"\n", buffer); + totalSize = expected_absolute_minimum_log_size; + } + } + close(sock); + + // logd allows excursions to 110% of total size + totalSize = (totalSize * 11 ) / 10; + + // 50% threshold for SPAM filter (<20% typical, lots of engineering margin) + ASSERT_GT(totalSize, nowSpamSize * 2); +} diff --git a/logwrapper/Android.mk b/logwrapper/Android.mk index 917bf37..61b4659 100644 --- a/logwrapper/Android.mk +++ b/logwrapper/Android.mk @@ -11,6 +11,7 @@ LOCAL_SRC_FILES := logwrap.c LOCAL_SHARED_LIBRARIES := libcutils liblog LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include LOCAL_C_INCLUDES := $(LOCAL_PATH)/include +LOCAL_CFLAGS := -Werror include $(BUILD_STATIC_LIBRARY) # ======================================================== @@ -22,6 +23,7 @@ LOCAL_SHARED_LIBRARIES := libcutils liblog LOCAL_WHOLE_STATIC_LIBRARIES := liblogwrap LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include LOCAL_C_INCLUDES := $(LOCAL_PATH)/include +LOCAL_CFLAGS := -Werror include $(BUILD_SHARED_LIBRARY) # ======================================================== @@ -31,4 +33,5 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= logwrapper.c LOCAL_MODULE := logwrapper LOCAL_STATIC_LIBRARIES := liblog liblogwrap libcutils +LOCAL_CFLAGS := -Werror include $(BUILD_EXECUTABLE) diff --git a/logwrapper/logwrap.c b/logwrapper/logwrap.c index 4ca1db4..d47c9b5 100644 --- a/logwrapper/logwrap.c +++ b/logwrapper/logwrap.c @@ -104,8 +104,6 @@ static void add_line_to_abbr_buf(struct abbr_buf *a_buf, char *linebuf, int line static int add_line_to_linear_buf(struct beginning_buf *b_buf, char *line, ssize_t line_len) { - size_t new_len; - char *new_buf; int full = 0; if ((line_len + b_buf->used_len) > b_buf->buf_size) { @@ -124,7 +122,6 @@ static void add_line_to_circular_buf(struct ending_buf *e_buf, { ssize_t free_len; ssize_t needed_space; - char *new_buf; int cnt; if (e_buf->buf == NULL) { @@ -192,7 +189,6 @@ static void print_buf_lines(struct log_info *log_info, char *buf, int buf_size) { char *line_start; char c; - int line_len; int i; line_start = buf; diff --git a/mkbootimg/Android.mk b/mkbootimg/Android.mk index 2a97c26..0c9b0c6 100644 --- a/mkbootimg/Android.mk +++ b/mkbootimg/Android.mk @@ -4,6 +4,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := mkbootimg.c LOCAL_STATIC_LIBRARIES := libmincrypt +LOCAL_CFLAGS := -Werror LOCAL_MODULE := mkbootimg diff --git a/mkbootimg/mkbootimg.c b/mkbootimg/mkbootimg.c index d598f03..fc92b4d 100644 --- a/mkbootimg/mkbootimg.c +++ b/mkbootimg/mkbootimg.c @@ -77,7 +77,7 @@ static unsigned char padding[16384] = { 0, }; int write_padding(int fd, unsigned pagesize, unsigned itemsize) { unsigned pagemask = pagesize - 1; - unsigned count; + ssize_t count; if((itemsize & pagemask) == 0) { return 0; @@ -108,7 +108,7 @@ int main(int argc, char **argv) unsigned pagesize = 2048; int fd; SHA_CTX ctx; - uint8_t* sha; + const uint8_t* sha; unsigned base = 0x10000000; unsigned kernel_offset = 0x00008000; unsigned ramdisk_offset = 0x01000000; @@ -189,7 +189,7 @@ int main(int argc, char **argv) return usage(); } - strcpy(hdr.name, board); + strcpy((char *) hdr.name, board); memcpy(hdr.magic, BOOT_MAGIC, BOOT_MAGIC_SIZE); @@ -255,15 +255,15 @@ int main(int argc, char **argv) if(write(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) goto fail; if(write_padding(fd, pagesize, sizeof(hdr))) goto fail; - if(write(fd, kernel_data, hdr.kernel_size) != hdr.kernel_size) goto fail; + if(write(fd, kernel_data, hdr.kernel_size) != (ssize_t) hdr.kernel_size) goto fail; if(write_padding(fd, pagesize, hdr.kernel_size)) goto fail; - if(write(fd, ramdisk_data, hdr.ramdisk_size) != hdr.ramdisk_size) goto fail; + if(write(fd, ramdisk_data, hdr.ramdisk_size) != (ssize_t) hdr.ramdisk_size) goto fail; if(write_padding(fd, pagesize, hdr.ramdisk_size)) goto fail; if(second_data) { - if(write(fd, second_data, hdr.second_size) != hdr.second_size) goto fail; - if(write_padding(fd, pagesize, hdr.ramdisk_size)) goto fail; + if(write(fd, second_data, hdr.second_size) != (ssize_t) hdr.second_size) goto fail; + if(write_padding(fd, pagesize, hdr.second_size)) goto fail; } return 0; diff --git a/netcfg/Android.mk b/netcfg/Android.mk index 949f417..fc01a54 100644 --- a/netcfg/Android.mk +++ b/netcfg/Android.mk @@ -11,6 +11,7 @@ LOCAL_MODULE:= netcfg #LOCAL_STATIC_LIBRARIES := libcutils libc LOCAL_SHARED_LIBRARIES := libc libnetutils +LOCAL_CFLAGS := -Werror include $(BUILD_EXECUTABLE) endif diff --git a/netcfg/netcfg.c b/netcfg/netcfg.c index 3738f24..2308f37 100644 --- a/netcfg/netcfg.c +++ b/netcfg/netcfg.c @@ -1,5 +1,4 @@ -/* system/bin/netcfg/netcfg.c -** +/* ** Copyright 2006, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,18 +14,14 @@ ** limitations under the License. */ -#include <stdio.h> -#include <stdlib.h> #include <errno.h> #include <dirent.h> #include <netinet/ether.h> #include <netinet/if_ether.h> - -#include <netutils/ifc.h> #include <netutils/dhcp.h> - -static int verbose = 0; - +#include <netutils/ifc.h> +#include <stdio.h> +#include <stdlib.h> void die(const char *reason) { diff --git a/reboot/Android.mk b/reboot/Android.mk index 4db0c1e..7a24f99 100644 --- a/reboot/Android.mk +++ b/reboot/Android.mk @@ -1,12 +1,14 @@ # Copyright 2013 The Android Open Source Project -LOCAL_PATH:= $(call my-dir) +LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -LOCAL_SRC_FILES:= reboot.c +LOCAL_SRC_FILES := reboot.c -LOCAL_SHARED_LIBRARIES:= libcutils +LOCAL_SHARED_LIBRARIES := libcutils -LOCAL_MODULE:= reboot +LOCAL_MODULE := reboot + +LOCAL_CFLAGS := -Werror include $(BUILD_EXECUTABLE) diff --git a/reboot/reboot.c b/reboot/reboot.c index d9a4227..007dfba 100644 --- a/reboot/reboot.c +++ b/reboot/reboot.c @@ -35,7 +35,7 @@ int main(int argc, char *argv[]) c = getopt(argc, argv, "p"); - if (c == EOF) { + if (c == -1) { break; } diff --git a/rootdir/Android.mk b/rootdir/Android.mk index 2c16084..aca08bf 100644 --- a/rootdir/Android.mk +++ b/rootdir/Android.mk @@ -30,9 +30,17 @@ LOCAL_POST_INSTALL_CMD := mkdir -p $(addprefix $(TARGET_ROOT_OUT)/, \ include $(BUILD_SYSTEM)/base_rules.mk -$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/init.environ.rc.in +# Regenerate init.environ.rc if PRODUCT_BOOTCLASSPATH has changed. +bcp_md5 := $(word 1, $(shell echo $(PRODUCT_BOOTCLASSPATH) | $(MD5SUM))) +bcp_dep := $(intermediates)/$(bcp_md5).bcp.dep +$(bcp_dep) : + $(hide) mkdir -p $(dir $@) && rm -rf $(dir $@)*.bcp.dep && touch $@ + +$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/init.environ.rc.in $(bcp_dep) @echo "Generate: $< -> $@" @mkdir -p $(dir $@) $(hide) sed -e 's?%BOOTCLASSPATH%?$(PRODUCT_BOOTCLASSPATH)?g' $< >$@ +bcp_md5 := +bcp_dep := ####################################### diff --git a/rootdir/init.environ.rc.in b/rootdir/init.environ.rc.in index 927c33d..1f964e3 100644 --- a/rootdir/init.environ.rc.in +++ b/rootdir/init.environ.rc.in @@ -9,3 +9,4 @@ on init export ASEC_MOUNTPOINT /mnt/asec export LOOP_MOUNTPOINT /mnt/obb export BOOTCLASSPATH %BOOTCLASSPATH% + export LD_PRELOAD libsigchain.so diff --git a/rootdir/init.rc b/rootdir/init.rc index 90c8187..b59e31f 100644 --- a/rootdir/init.rc +++ b/rootdir/init.rc @@ -7,6 +7,7 @@ import /init.environ.rc import /init.usb.rc import /init.${ro.hardware}.rc +import /init.${ro.zygote}.rc import /init.trace.rc on early-init @@ -25,29 +26,28 @@ on early-init start ueventd -# create mountpoints + # create mountpoints mkdir /mnt 0775 root system on init + sysclktz 0 -sysclktz 0 + loglevel 3 -loglevel 3 - -# Backward compatibility + # Backward compatibility symlink /system/etc /etc symlink /sys/kernel/debug /d -# Right now vendor lives on the same filesystem as system, -# but someday that may change. + # Right now vendor lives on the same filesystem as system, + # but someday that may change. symlink /system/vendor /vendor -# Create cgroup mount point for cpu accounting + # Create cgroup mount point for cpu accounting mkdir /acct mount cgroup none /acct cpuacct mkdir /acct/uid -# Create cgroup mount point for memory + # Create cgroup mount point for memory mount tmpfs none /sys/fs/cgroup mode=0750,uid=0,gid=1000 mkdir /sys/fs/cgroup/memory 0750 root system mount cgroup none /sys/fs/cgroup/memory memory @@ -101,13 +101,20 @@ loglevel 3 write /proc/sys/kernel/sched_child_runs_first 0 write /proc/sys/kernel/randomize_va_space 2 write /proc/sys/kernel/kptr_restrict 2 - write /proc/sys/kernel/dmesg_restrict 1 write /proc/sys/vm/mmap_min_addr 32768 write /proc/sys/net/ipv4/ping_group_range "0 2147483647" + write /proc/sys/net/unix/max_dgram_qlen 300 write /proc/sys/kernel/sched_rt_runtime_us 950000 write /proc/sys/kernel/sched_rt_period_us 1000000 -# Create cgroup mount points for process groups + # reflect fwmark from incoming packets onto generated replies + write /proc/sys/net/ipv4/fwmark_reflect 1 + write /proc/sys/net/ipv6/fwmark_reflect 1 + + # set fwmark on accepted sockets + write /proc/sys/net/ipv4/tcp_fwmark_accept 1 + + # Create cgroup mount points for process groups mkdir /dev/cpuctl mount cgroup none /dev/cpuctl cpu chown system system /dev/cpuctl @@ -132,25 +139,50 @@ loglevel 3 write /dev/cpuctl/apps/bg_non_interactive/cpu.rt_runtime_us 700000 write /dev/cpuctl/apps/bg_non_interactive/cpu.rt_period_us 1000000 -# qtaguid will limit access to specific data based on group memberships. -# net_bw_acct grants impersonation of socket owners. -# net_bw_stats grants access to other apps' detailed tagged-socket stats. + # qtaguid will limit access to specific data based on group memberships. + # net_bw_acct grants impersonation of socket owners. + # net_bw_stats grants access to other apps' detailed tagged-socket stats. chown root net_bw_acct /proc/net/xt_qtaguid/ctrl chown root net_bw_stats /proc/net/xt_qtaguid/stats -# Allow everybody to read the xt_qtaguid resource tracking misc dev. -# This is needed by any process that uses socket tagging. + # Allow everybody to read the xt_qtaguid resource tracking misc dev. + # This is needed by any process that uses socket tagging. chmod 0644 /dev/xt_qtaguid -# Create location for fs_mgr to store abbreviated output from filesystem -# checker programs. + # Create location for fs_mgr to store abbreviated output from filesystem + # checker programs. mkdir /dev/fscklogs 0770 root system -# pstore/ramoops previous console log + # pstore/ramoops previous console log mount pstore pstore /sys/fs/pstore chown system log /sys/fs/pstore/console-ramoops chmod 0440 /sys/fs/pstore/console-ramoops +# Healthd can trigger a full boot from charger mode by signaling this +# property when the power button is held. +on property:sys.boot_from_charger_mode=1 + class_stop charger + trigger late-init + +# Load properties from /system/ + /factory after fs mount. +on load_all_props_action + load_all_props + +# Mount filesystems and start core system services. +on late-init + trigger early-fs + trigger fs + trigger post-fs + trigger post-fs-data + + # Load properties from /system/ + /factory after fs mount. Place + # this in another action so that the load will be scheduled after the prior + # issued fs triggers have completed. + trigger load_all_props_action + + trigger early-boot + trigger boot + on post-fs # once everything is setup, no need to modify / mount rootfs rootfs / ro remount @@ -161,13 +193,11 @@ on post-fs chown system cache /cache chmod 0770 /cache # We restorecon /cache in case the cache partition has been reset. - restorecon /cache + restorecon_recursive /cache # This may have been created by the recovery system with odd permissions chown system cache /cache/recovery chmod 0770 /cache/recovery - # This may have been created by the recovery system with the wrong context. - restorecon /cache/recovery #change permissions on vmallocinfo so we can grab it from bugreports chown root log /proc/vmallocinfo @@ -184,6 +214,9 @@ on post-fs chown system log /proc/last_kmsg chmod 0440 /proc/last_kmsg + # make the selinux kernel policy world-readable + chmod 0444 /sys/fs/selinux/policy + # create the lost+found directories, so as to enforce our permissions mkdir /cache/lost+found 0770 root root @@ -220,26 +253,22 @@ on post-fs-data mkdir /data/misc/bluetooth 0770 system system mkdir /data/misc/keystore 0700 keystore keystore mkdir /data/misc/keychain 0771 system system + mkdir /data/misc/net 0750 root shell mkdir /data/misc/radio 0770 system radio mkdir /data/misc/sms 0770 system radio mkdir /data/misc/zoneinfo 0775 system system - restorecon_recursive /data/misc/zoneinfo mkdir /data/misc/vpn 0770 system vpn + mkdir /data/misc/shared_relro 0771 shared_relro shared_relro mkdir /data/misc/systemkeys 0700 system system mkdir /data/misc/wifi 0770 wifi wifi mkdir /data/misc/wifi/sockets 0770 wifi wifi - restorecon_recursive /data/misc/wifi/sockets mkdir /data/misc/wifi/wpa_supplicant 0770 wifi wifi mkdir /data/misc/dhcp 0770 dhcp dhcp + mkdir /data/misc/user 0771 root root # give system access to wpa_supplicant.conf for backup and restore chmod 0660 /data/misc/wifi/wpa_supplicant.conf mkdir /data/local 0751 root root mkdir /data/misc/media 0700 media media - restorecon_recursive /data/misc/media - - # Set security context of any pre-existing /data/misc/adb/adb_keys file. - restorecon /data/misc/adb - restorecon /data/misc/adb/adb_keys # For security reasons, /data/local/tmp should always be empty. # Do not place files or directories in /data/local/tmp @@ -250,11 +279,10 @@ on post-fs-data mkdir /data/app-lib 0771 system system mkdir /data/app 0771 system system mkdir /data/property 0700 root root - mkdir /data/ssh 0750 root shell - mkdir /data/ssh/empty 0700 root root # create dalvik-cache, so as to enforce our permissions mkdir /data/dalvik-cache 0771 system system + mkdir /data/dalvik-cache/profiles 0711 system system # create resource-cache and double-check the perms mkdir /data/resource-cache 0771 system system @@ -271,7 +299,6 @@ on post-fs-data # create directory for MediaDrm plug-ins - give drm the read/write access to # the following directory. mkdir /data/mediadrm 0770 mediadrm mediadrm - restorecon_recursive /data/mediadrm # symlink to bugreport storage location symlink /data/data/com.android.shell/files/bugreports /data/bugreports @@ -282,6 +309,9 @@ on post-fs-data # Reload policy from /data/security if present. setprop selinux.reload_policy 1 + # Set SELinux security contexts on upgrade or policy update. + restorecon_recursive /data + # If there is no fs-post-data action in the init.<device>.rc file, you # must uncomment this line, otherwise encrypted filesystems # won't work. @@ -289,17 +319,17 @@ on post-fs-data #setprop vold.post_fs_data_done 1 on boot -# basic network init + # basic network init ifup lo hostname localhost domainname localdomain -# set RLIMIT_NICE to allow priorities from 19 to -20 + # set RLIMIT_NICE to allow priorities from 19 to -20 setrlimit 13 40 40 -# Memory management. Basic kernel parameters, and allow the high -# level system server to be able to adjust the kernel OOM driver -# parameters to match how it is managing things. + # Memory management. Basic kernel parameters, and allow the high + # level system server to be able to adjust the kernel OOM driver + # parameters to match how it is managing things. write /proc/sys/vm/overcommit_memory 1 write /proc/sys/vm/min_free_order_shift 4 chown root system /sys/module/lowmemorykiller/parameters/adj @@ -375,22 +405,22 @@ on boot chown system system /sys/kernel/ipv4/tcp_rmem_max chown root radio /proc/cmdline -# Define TCP buffer sizes for various networks -# ReadMin, ReadInitial, ReadMax, WriteMin, WriteInitial, WriteMax, + # Define TCP buffer sizes for various networks + # ReadMin, ReadInitial, ReadMax, WriteMin, WriteInitial, WriteMax, setprop net.tcp.buffersize.default 4096,87380,110208,4096,16384,110208 setprop net.tcp.buffersize.wifi 524288,1048576,2097152,262144,524288,1048576 setprop net.tcp.buffersize.ethernet 524288,1048576,3145728,524288,1048576,2097152 setprop net.tcp.buffersize.lte 524288,1048576,2097152,262144,524288,1048576 - setprop net.tcp.buffersize.umts 4094,87380,110208,4096,16384,110208 - setprop net.tcp.buffersize.hspa 4094,87380,262144,4096,16384,262144 - setprop net.tcp.buffersize.hsupa 4094,87380,262144,4096,16384,262144 - setprop net.tcp.buffersize.hsdpa 4094,87380,262144,4096,16384,262144 - setprop net.tcp.buffersize.hspap 4094,87380,1220608,4096,16384,1220608 - setprop net.tcp.buffersize.edge 4093,26280,35040,4096,16384,35040 - setprop net.tcp.buffersize.gprs 4092,8760,11680,4096,8760,11680 + setprop net.tcp.buffersize.umts 58254,349525,1048576,58254,349525,1048576 + setprop net.tcp.buffersize.hspa 40778,244668,734003,16777,100663,301990 + setprop net.tcp.buffersize.hsupa 40778,244668,734003,16777,100663,301990 + setprop net.tcp.buffersize.hsdpa 61167,367002,1101005,8738,52429,262114 + setprop net.tcp.buffersize.hspap 122334,734003,2202010,32040,192239,576717 + setprop net.tcp.buffersize.edge 4093,26280,70800,4096,16384,70800 + setprop net.tcp.buffersize.gprs 4092,8760,48000,4096,8760,48000 setprop net.tcp.buffersize.evdo 4094,87380,262144,4096,16384,262144 -# Define default initial receive window size in segments. + # Define default initial receive window size in segments. setprop net.tcp.default_init_rwnd 60 class_start core @@ -399,6 +429,9 @@ on boot on nonencrypted class_start late_start +on property:sys.init_log_level=* + loglevel ${sys.init_log_level} + on charger class_start charger @@ -430,6 +463,7 @@ on property:sys.powerctl=* # So proxy writes through init. on property:sys.sysctl.extra_free_kbytes=* write /proc/sys/vm/extra_free_kbytes ${sys.sysctl.extra_free_kbytes} + # "tcp_default_init_rwnd" Is too long! on property:sys.sysctl.tcp_def_init_rwnd=* write /proc/sys/net/ipv4/tcp_default_init_rwnd ${sys.sysctl.tcp_def_init_rwnd} @@ -442,6 +476,13 @@ service ueventd /sbin/ueventd critical seclabel u:r:ueventd:s0 +service logd /system/bin/logd + class core + socket logd stream 0666 logd logd + socket logdr seqpacket 0666 logd logd + socket logdw dgram 0222 logd logd + seclabel u:r:logd:s0 + service healthd /sbin/healthd class core critical @@ -457,7 +498,7 @@ service console /system/bin/sh console disabled user shell - group log + group shell log seclabel u:r:shell:s0 on property:ro.debuggable=1 @@ -500,10 +541,14 @@ service netd /system/bin/netd socket netd stream 0660 root system socket dnsproxyd stream 0660 root inet socket mdns stream 0660 root system + socket fwmarkd stream 0660 root inet service debuggerd /system/bin/debuggerd class main +service debuggerd64 /system/bin/debuggerd64 + class main + service ril-daemon /system/bin/rild class main socket rild stream 660 root radio @@ -517,14 +562,6 @@ service surfaceflinger /system/bin/surfaceflinger group graphics drmrpc onrestart restart zygote -service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server - class main - socket zygote stream 660 root system - onrestart write /sys/android_power/request_state wake - onrestart write /sys/power/state on - onrestart restart media - onrestart restart netd - service drm /system/bin/drmserver class main user drm @@ -578,10 +615,6 @@ service dumpstate /system/bin/dumpstate -s disabled oneshot -service sshd /system/bin/start-ssh - class main - disabled - service mdnsd /system/bin/mdnsd class main user mdnsr diff --git a/rootdir/init.zygote32.rc b/rootdir/init.zygote32.rc new file mode 100644 index 0000000..75961e6 --- /dev/null +++ b/rootdir/init.zygote32.rc @@ -0,0 +1,8 @@ +service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server + class main + socket zygote stream 660 root system + onrestart write /sys/android_power/request_state wake + onrestart write /sys/power/state on + onrestart restart media + onrestart restart netd + diff --git a/rootdir/init.zygote32_64.rc b/rootdir/init.zygote32_64.rc new file mode 100644 index 0000000..68c0668 --- /dev/null +++ b/rootdir/init.zygote32_64.rc @@ -0,0 +1,12 @@ +service zygote /system/bin/app_process32 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote + class main + socket zygote stream 660 root system + onrestart write /sys/android_power/request_state wake + onrestart write /sys/power/state on + onrestart restart media + onrestart restart netd + +service zygote_secondary /system/bin/app_process64 -Xzygote /system/bin --zygote --socket-name=zygote_secondary + class main + socket zygote_secondary stream 660 root system + onrestart restart zygote diff --git a/rootdir/init.zygote64.rc b/rootdir/init.zygote64.rc new file mode 100644 index 0000000..afb6d63 --- /dev/null +++ b/rootdir/init.zygote64.rc @@ -0,0 +1,8 @@ +service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server + class main + socket zygote stream 660 root system + onrestart write /sys/android_power/request_state wake + onrestart write /sys/power/state on + onrestart restart media + onrestart restart netd + diff --git a/rootdir/init.zygote64_32.rc b/rootdir/init.zygote64_32.rc new file mode 100644 index 0000000..979ab3b --- /dev/null +++ b/rootdir/init.zygote64_32.rc @@ -0,0 +1,12 @@ +service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote + class main + socket zygote stream 660 root system + onrestart write /sys/android_power/request_state wake + onrestart write /sys/power/state on + onrestart restart media + onrestart restart netd + +service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary + class main + socket zygote_secondary stream 660 root system + onrestart restart zygote diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc index b8fe716..eff24c3 100644 --- a/rootdir/ueventd.rc +++ b/rootdir/ueventd.rc @@ -91,3 +91,5 @@ subsystem adf /sys/devices/virtual/input/input* enable 0660 root input /sys/devices/virtual/input/input* poll_delay 0660 root input /sys/devices/virtual/usb_composite/* enable 0664 root system +/sys/devices/system/cpu/cpu* cpufreq/scaling_max_freq 0664 system system +/sys/devices/system/cpu/cpu* cpufreq/scaling_min_freq 0664 system system diff --git a/run-as/Android.mk b/run-as/Android.mk index a8f2885..3774acc 100644 --- a/run-as/Android.mk +++ b/run-as/Android.mk @@ -1,10 +1,12 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) -LOCAL_SRC_FILES:= run-as.c package.c +LOCAL_SRC_FILES := run-as.c package.c LOCAL_SHARED_LIBRARIES := libselinux -LOCAL_MODULE:= run-as +LOCAL_MODULE := run-as + +LOCAL_CFLAGS := -Werror include $(BUILD_EXECUTABLE) diff --git a/run-as/package.c b/run-as/package.c index 901e9e3..4f8f3a7 100644 --- a/run-as/package.c +++ b/run-as/package.c @@ -128,7 +128,9 @@ map_file(const char* filename, size_t* filesize) } /* Memory-map the file now */ - address = TEMP_FAILURE_RETRY(mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, 0)); + do { + address = mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, 0); + } while (address == MAP_FAILED && errno == EINTR); if (address == MAP_FAILED) { address = NULL; goto EXIT; @@ -408,10 +410,6 @@ parse_positive_decimal(const char** pp, const char* end) value = -1; } return value; - -BAD: - *pp = p; - return -1; } /* Read the system's package database and extract information about diff --git a/sdcard/Android.mk b/sdcard/Android.mk index 4630db9..63b0f41 100644 --- a/sdcard/Android.mk +++ b/sdcard/Android.mk @@ -1,10 +1,10 @@ -LOCAL_PATH:= $(call my-dir) +LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -LOCAL_SRC_FILES:= sdcard.c -LOCAL_MODULE:= sdcard -LOCAL_CFLAGS := -Wall -Wno-unused-parameter +LOCAL_SRC_FILES := sdcard.c +LOCAL_MODULE := sdcard +LOCAL_CFLAGS := -Wall -Wno-unused-parameter -Werror LOCAL_SHARED_LIBRARIES := libc libcutils diff --git a/sdcard/fuse.h b/sdcard/fuse.h deleted file mode 100644 index 3138da9..0000000 --- a/sdcard/fuse.h +++ /dev/null @@ -1,578 +0,0 @@ -/* - FUSE: Filesystem in Userspace - Copyright (C) 2001-2008 Miklos Szeredi <miklos@szeredi.hu> - - This program can be distributed under the terms of the GNU GPL. - See the file COPYING. -*/ - -/* - * from the libfuse FAQ (and consistent with the Linux Kernel license): - * - * Under what conditions may I distribute a filesystem that uses the - * raw kernel interface of FUSE? - * - * There are no restrictions whatsoever for using the raw kernel interface. - * - */ - -/* - * This file defines the kernel interface of FUSE - * - * Protocol changelog: - * - * 7.9: - * - new fuse_getattr_in input argument of GETATTR - * - add lk_flags in fuse_lk_in - * - add lock_owner field to fuse_setattr_in, fuse_read_in and fuse_write_in - * - add blksize field to fuse_attr - * - add file flags field to fuse_read_in and fuse_write_in - * - * 7.10 - * - add nonseekable open flag - * - * 7.11 - * - add IOCTL message - * - add unsolicited notification support - * - add POLL message and NOTIFY_POLL notification - * - * 7.12 - * - add umask flag to input argument of open, mknod and mkdir - * - add notification messages for invalidation of inodes and - * directory entries - * - * 7.13 - * - make max number of background requests and congestion threshold - * tunables - */ - -#ifndef _LINUX_FUSE_H -#define _LINUX_FUSE_H - -#include <linux/types.h> - -/* - * Version negotiation: - * - * Both the kernel and userspace send the version they support in the - * INIT request and reply respectively. - * - * If the major versions match then both shall use the smallest - * of the two minor versions for communication. - * - * If the kernel supports a larger major version, then userspace shall - * reply with the major version it supports, ignore the rest of the - * INIT message and expect a new INIT message from the kernel with a - * matching major version. - * - * If the library supports a larger major version, then it shall fall - * back to the major protocol version sent by the kernel for - * communication and reply with that major version (and an arbitrary - * supported minor version). - */ - -/** Version number of this interface */ -#define FUSE_KERNEL_VERSION 7 - -/** Minor version number of this interface */ -#define FUSE_KERNEL_MINOR_VERSION 13 - -/** The node ID of the root inode */ -#define FUSE_ROOT_ID 1 - -/* Make sure all structures are padded to 64bit boundary, so 32bit - userspace works under 64bit kernels */ - -struct fuse_attr { - __u64 ino; - __u64 size; - __u64 blocks; - __u64 atime; - __u64 mtime; - __u64 ctime; - __u32 atimensec; - __u32 mtimensec; - __u32 ctimensec; - __u32 mode; - __u32 nlink; - __u32 uid; - __u32 gid; - __u32 rdev; - __u32 blksize; - __u32 padding; -}; - -struct fuse_kstatfs { - __u64 blocks; - __u64 bfree; - __u64 bavail; - __u64 files; - __u64 ffree; - __u32 bsize; - __u32 namelen; - __u32 frsize; - __u32 padding; - __u32 spare[6]; -}; - -struct fuse_file_lock { - __u64 start; - __u64 end; - __u32 type; - __u32 pid; /* tgid */ -}; - -/** - * Bitmasks for fuse_setattr_in.valid - */ -#define FATTR_MODE (1 << 0) -#define FATTR_UID (1 << 1) -#define FATTR_GID (1 << 2) -#define FATTR_SIZE (1 << 3) -#define FATTR_ATIME (1 << 4) -#define FATTR_MTIME (1 << 5) -#define FATTR_FH (1 << 6) -#define FATTR_ATIME_NOW (1 << 7) -#define FATTR_MTIME_NOW (1 << 8) -#define FATTR_LOCKOWNER (1 << 9) - -/** - * Flags returned by the OPEN request - * - * FOPEN_DIRECT_IO: bypass page cache for this open file - * FOPEN_KEEP_CACHE: don't invalidate the data cache on open - * FOPEN_NONSEEKABLE: the file is not seekable - */ -#define FOPEN_DIRECT_IO (1 << 0) -#define FOPEN_KEEP_CACHE (1 << 1) -#define FOPEN_NONSEEKABLE (1 << 2) - -/** - * INIT request/reply flags - * - * FUSE_EXPORT_SUPPORT: filesystem handles lookups of "." and ".." - * FUSE_DONT_MASK: don't apply umask to file mode on create operations - */ -#define FUSE_ASYNC_READ (1 << 0) -#define FUSE_POSIX_LOCKS (1 << 1) -#define FUSE_FILE_OPS (1 << 2) -#define FUSE_ATOMIC_O_TRUNC (1 << 3) -#define FUSE_EXPORT_SUPPORT (1 << 4) -#define FUSE_BIG_WRITES (1 << 5) -#define FUSE_DONT_MASK (1 << 6) - -/** - * CUSE INIT request/reply flags - * - * CUSE_UNRESTRICTED_IOCTL: use unrestricted ioctl - */ -#define CUSE_UNRESTRICTED_IOCTL (1 << 0) - -/** - * Release flags - */ -#define FUSE_RELEASE_FLUSH (1 << 0) - -/** - * Getattr flags - */ -#define FUSE_GETATTR_FH (1 << 0) - -/** - * Lock flags - */ -#define FUSE_LK_FLOCK (1 << 0) - -/** - * WRITE flags - * - * FUSE_WRITE_CACHE: delayed write from page cache, file handle is guessed - * FUSE_WRITE_LOCKOWNER: lock_owner field is valid - */ -#define FUSE_WRITE_CACHE (1 << 0) -#define FUSE_WRITE_LOCKOWNER (1 << 1) - -/** - * Read flags - */ -#define FUSE_READ_LOCKOWNER (1 << 1) - -/** - * Ioctl flags - * - * FUSE_IOCTL_COMPAT: 32bit compat ioctl on 64bit machine - * FUSE_IOCTL_UNRESTRICTED: not restricted to well-formed ioctls, retry allowed - * FUSE_IOCTL_RETRY: retry with new iovecs - * - * FUSE_IOCTL_MAX_IOV: maximum of in_iovecs + out_iovecs - */ -#define FUSE_IOCTL_COMPAT (1 << 0) -#define FUSE_IOCTL_UNRESTRICTED (1 << 1) -#define FUSE_IOCTL_RETRY (1 << 2) - -#define FUSE_IOCTL_MAX_IOV 256 - -/** - * Poll flags - * - * FUSE_POLL_SCHEDULE_NOTIFY: request poll notify - */ -#define FUSE_POLL_SCHEDULE_NOTIFY (1 << 0) - -enum fuse_opcode { - FUSE_LOOKUP = 1, - FUSE_FORGET = 2, /* no reply */ - FUSE_GETATTR = 3, - FUSE_SETATTR = 4, - FUSE_READLINK = 5, - FUSE_SYMLINK = 6, - FUSE_MKNOD = 8, - FUSE_MKDIR = 9, - FUSE_UNLINK = 10, - FUSE_RMDIR = 11, - FUSE_RENAME = 12, - FUSE_LINK = 13, - FUSE_OPEN = 14, - FUSE_READ = 15, - FUSE_WRITE = 16, - FUSE_STATFS = 17, - FUSE_RELEASE = 18, - FUSE_FSYNC = 20, - FUSE_SETXATTR = 21, - FUSE_GETXATTR = 22, - FUSE_LISTXATTR = 23, - FUSE_REMOVEXATTR = 24, - FUSE_FLUSH = 25, - FUSE_INIT = 26, - FUSE_OPENDIR = 27, - FUSE_READDIR = 28, - FUSE_RELEASEDIR = 29, - FUSE_FSYNCDIR = 30, - FUSE_GETLK = 31, - FUSE_SETLK = 32, - FUSE_SETLKW = 33, - FUSE_ACCESS = 34, - FUSE_CREATE = 35, - FUSE_INTERRUPT = 36, - FUSE_BMAP = 37, - FUSE_DESTROY = 38, - FUSE_IOCTL = 39, - FUSE_POLL = 40, - - /* CUSE specific operations */ - CUSE_INIT = 4096, -}; - -enum fuse_notify_code { - FUSE_NOTIFY_POLL = 1, - FUSE_NOTIFY_INVAL_INODE = 2, - FUSE_NOTIFY_INVAL_ENTRY = 3, - FUSE_NOTIFY_CODE_MAX, -}; - -/* The read buffer is required to be at least 8k, but may be much larger */ -#define FUSE_MIN_READ_BUFFER 8192 - -#define FUSE_COMPAT_ENTRY_OUT_SIZE 120 - -struct fuse_entry_out { - __u64 nodeid; /* Inode ID */ - __u64 generation; /* Inode generation: nodeid:gen must - be unique for the fs's lifetime */ - __u64 entry_valid; /* Cache timeout for the name */ - __u64 attr_valid; /* Cache timeout for the attributes */ - __u32 entry_valid_nsec; - __u32 attr_valid_nsec; - struct fuse_attr attr; -}; - -struct fuse_forget_in { - __u64 nlookup; -}; - -struct fuse_getattr_in { - __u32 getattr_flags; - __u32 dummy; - __u64 fh; -}; - -#define FUSE_COMPAT_ATTR_OUT_SIZE 96 - -struct fuse_attr_out { - __u64 attr_valid; /* Cache timeout for the attributes */ - __u32 attr_valid_nsec; - __u32 dummy; - struct fuse_attr attr; -}; - -#define FUSE_COMPAT_MKNOD_IN_SIZE 8 - -struct fuse_mknod_in { - __u32 mode; - __u32 rdev; - __u32 umask; - __u32 padding; -}; - -struct fuse_mkdir_in { - __u32 mode; - __u32 umask; -}; - -struct fuse_rename_in { - __u64 newdir; -}; - -struct fuse_link_in { - __u64 oldnodeid; -}; - -struct fuse_setattr_in { - __u32 valid; - __u32 padding; - __u64 fh; - __u64 size; - __u64 lock_owner; - __u64 atime; - __u64 mtime; - __u64 unused2; - __u32 atimensec; - __u32 mtimensec; - __u32 unused3; - __u32 mode; - __u32 unused4; - __u32 uid; - __u32 gid; - __u32 unused5; -}; - -struct fuse_open_in { - __u32 flags; - __u32 unused; -}; - -struct fuse_create_in { - __u32 flags; - __u32 mode; - __u32 umask; - __u32 padding; -}; - -struct fuse_open_out { - __u64 fh; - __u32 open_flags; - __u32 padding; -}; - -struct fuse_release_in { - __u64 fh; - __u32 flags; - __u32 release_flags; - __u64 lock_owner; -}; - -struct fuse_flush_in { - __u64 fh; - __u32 unused; - __u32 padding; - __u64 lock_owner; -}; - -struct fuse_read_in { - __u64 fh; - __u64 offset; - __u32 size; - __u32 read_flags; - __u64 lock_owner; - __u32 flags; - __u32 padding; -}; - -#define FUSE_COMPAT_WRITE_IN_SIZE 24 - -struct fuse_write_in { - __u64 fh; - __u64 offset; - __u32 size; - __u32 write_flags; - __u64 lock_owner; - __u32 flags; - __u32 padding; -}; - -struct fuse_write_out { - __u32 size; - __u32 padding; -}; - -#define FUSE_COMPAT_STATFS_SIZE 48 - -struct fuse_statfs_out { - struct fuse_kstatfs st; -}; - -struct fuse_fsync_in { - __u64 fh; - __u32 fsync_flags; - __u32 padding; -}; - -struct fuse_setxattr_in { - __u32 size; - __u32 flags; -}; - -struct fuse_getxattr_in { - __u32 size; - __u32 padding; -}; - -struct fuse_getxattr_out { - __u32 size; - __u32 padding; -}; - -struct fuse_lk_in { - __u64 fh; - __u64 owner; - struct fuse_file_lock lk; - __u32 lk_flags; - __u32 padding; -}; - -struct fuse_lk_out { - struct fuse_file_lock lk; -}; - -struct fuse_access_in { - __u32 mask; - __u32 padding; -}; - -struct fuse_init_in { - __u32 major; - __u32 minor; - __u32 max_readahead; - __u32 flags; -}; - -struct fuse_init_out { - __u32 major; - __u32 minor; - __u32 max_readahead; - __u32 flags; - __u16 max_background; - __u16 congestion_threshold; - __u32 max_write; -}; - -#define CUSE_INIT_INFO_MAX 4096 - -struct cuse_init_in { - __u32 major; - __u32 minor; - __u32 unused; - __u32 flags; -}; - -struct cuse_init_out { - __u32 major; - __u32 minor; - __u32 unused; - __u32 flags; - __u32 max_read; - __u32 max_write; - __u32 dev_major; /* chardev major */ - __u32 dev_minor; /* chardev minor */ - __u32 spare[10]; -}; - -struct fuse_interrupt_in { - __u64 unique; -}; - -struct fuse_bmap_in { - __u64 block; - __u32 blocksize; - __u32 padding; -}; - -struct fuse_bmap_out { - __u64 block; -}; - -struct fuse_ioctl_in { - __u64 fh; - __u32 flags; - __u32 cmd; - __u64 arg; - __u32 in_size; - __u32 out_size; -}; - -struct fuse_ioctl_out { - __s32 result; - __u32 flags; - __u32 in_iovs; - __u32 out_iovs; -}; - -struct fuse_poll_in { - __u64 fh; - __u64 kh; - __u32 flags; - __u32 padding; -}; - -struct fuse_poll_out { - __u32 revents; - __u32 padding; -}; - -struct fuse_notify_poll_wakeup_out { - __u64 kh; -}; - -struct fuse_in_header { - __u32 len; - __u32 opcode; - __u64 unique; - __u64 nodeid; - __u32 uid; - __u32 gid; - __u32 pid; - __u32 padding; -}; - -struct fuse_out_header { - __u32 len; - __s32 error; - __u64 unique; -}; - -struct fuse_dirent { - __u64 ino; - __u64 off; - __u32 namelen; - __u32 type; - char name[0]; -}; - -#define FUSE_NAME_OFFSET offsetof(struct fuse_dirent, name) -#define FUSE_DIRENT_ALIGN(x) (((x) + sizeof(__u64) - 1) & ~(sizeof(__u64) - 1)) -#define FUSE_DIRENT_SIZE(d) \ - FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen) - -struct fuse_notify_inval_inode_out { - __u64 ino; - __s64 off; - __s64 len; -}; - -struct fuse_notify_inval_entry_out { - __u64 parent; - __u32 namelen; - __u32 padding; -}; - -#endif /* _LINUX_FUSE_H */ diff --git a/sdcard/sdcard.c b/sdcard/sdcard.c index 05fbfba..844ca65 100644 --- a/sdcard/sdcard.c +++ b/sdcard/sdcard.c @@ -14,39 +14,41 @@ * limitations under the License. */ +#define LOG_TAG "sdcard" + +#include <ctype.h> +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <linux/fuse.h> +#include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <unistd.h> -#include <errno.h> -#include <fcntl.h> +#include <sys/inotify.h> #include <sys/mount.h> +#include <sys/resource.h> #include <sys/stat.h> #include <sys/statfs.h> -#include <sys/uio.h> -#include <dirent.h> -#include <limits.h> -#include <ctype.h> -#include <pthread.h> #include <sys/time.h> -#include <sys/resource.h> -#include <sys/inotify.h> +#include <sys/uio.h> +#include <unistd.h> #include <cutils/fs.h> #include <cutils/hashmap.h> +#include <cutils/log.h> #include <cutils/multiuser.h> #include <private/android_filesystem_config.h> -#include "fuse.h" - /* README * * What is this? - * + * * sdcard is a program that uses FUSE to emulate FAT-on-sdcard style - * directory permissions (all files are given fixed owner, group, and - * permissions at creation, owner, group, and permissions are not + * directory permissions (all files are given fixed owner, group, and + * permissions at creation, owner, group, and permissions are not * changeable, symlinks and hardlinks are not createable, etc. * * See usage() for command line options. @@ -91,12 +93,12 @@ #define FUSE_TRACE 0 #if FUSE_TRACE -#define TRACE(x...) fprintf(stderr,x) +#define TRACE(x...) ALOGD(x) #else #define TRACE(x...) do {} while (0) #endif -#define ERROR(x...) fprintf(stderr,x) +#define ERROR(x...) ALOGE(x) #define FUSE_UNKNOWN_INO 0xffffffff @@ -200,7 +202,7 @@ static bool str_icase_equals(void *keyA, void *keyB) { } static int int_hash(void *key) { - return (int) key; + return (int) (uintptr_t) key; } static bool int_equals(void *keyA, void *keyB) { @@ -232,7 +234,7 @@ struct fuse_handler { * buffer at the same time. This allows us to share the underlying storage. */ union { __u8 request_buffer[MAX_REQUEST_SIZE]; - __u8 read_buffer[MAX_READ]; + __u8 read_buffer[MAX_READ + PAGESIZE]; }; }; @@ -487,7 +489,7 @@ static void derive_permissions_locked(struct fuse* fuse, struct node *parent, break; case PERM_ANDROID_DATA: case PERM_ANDROID_OBB: - appid = (appid_t) hashmapGet(fuse->package_to_appid, node->name); + appid = (appid_t) (uintptr_t) hashmapGet(fuse->package_to_appid, node->name); if (appid != 0) { node->uid = multiuser_get_uid(parent->userid, appid); } @@ -511,7 +513,7 @@ static bool get_caller_has_rw_locked(struct fuse* fuse, const struct fuse_in_hea } appid_t appid = multiuser_get_app_id(hdr->uid); - return hashmapContainsKey(fuse->appid_with_rw, (void*) appid); + return hashmapContainsKey(fuse->appid_with_rw, (void*) (uintptr_t) appid); } /* Kernel has already enforced everything we returned through @@ -1218,6 +1220,7 @@ static int handle_read(struct fuse* fuse, struct fuse_handler* handler, __u32 size = req->size; __u64 offset = req->offset; int res; + __u8 *read_buffer = (__u8 *) ((uintptr_t)(handler->read_buffer + PAGESIZE) & ~((uintptr_t)PAGESIZE-1)); /* Don't access any other fields of hdr or req beyond this point, the read buffer * overlaps the request buffer and will clobber data in the request. This @@ -1225,14 +1228,14 @@ static int handle_read(struct fuse* fuse, struct fuse_handler* handler, TRACE("[%d] READ %p(%d) %u@%llu\n", handler->token, h, h->fd, size, offset); - if (size > sizeof(handler->read_buffer)) { + if (size > MAX_READ) { return -EINVAL; } - res = pread64(h->fd, handler->read_buffer, size, offset); + res = pread64(h->fd, read_buffer, size, offset); if (res < 0) { return -errno; } - fuse_reply(fuse, unique, handler->read_buffer, res); + fuse_reply(fuse, unique, read_buffer, res); return NO_STATUS; } @@ -1243,6 +1246,12 @@ static int handle_write(struct fuse* fuse, struct fuse_handler* handler, struct fuse_write_out out; struct handle *h = id_to_ptr(req->fh); int res; + __u8 aligned_buffer[req->size] __attribute__((__aligned__(PAGESIZE))); + + if (req->flags & O_DIRECT) { + memcpy(aligned_buffer, buffer, req->size); + buffer = (const __u8*) aligned_buffer; + } TRACE("[%d] WRITE %p(%d) %u@%llu\n", handler->token, h, h->fd, req->size, req->offset); @@ -1300,14 +1309,23 @@ static int handle_release(struct fuse* fuse, struct fuse_handler* handler, static int handle_fsync(struct fuse* fuse, struct fuse_handler* handler, const struct fuse_in_header* hdr, const struct fuse_fsync_in* req) { - int is_data_sync = req->fsync_flags & 1; - struct handle *h = id_to_ptr(req->fh); - int res; + bool is_dir = (hdr->opcode == FUSE_FSYNCDIR); + bool is_data_sync = req->fsync_flags & 1; - TRACE("[%d] FSYNC %p(%d) is_data_sync=%d\n", handler->token, - h, h->fd, is_data_sync); - res = is_data_sync ? fdatasync(h->fd) : fsync(h->fd); - if (res < 0) { + int fd = -1; + if (is_dir) { + struct dirhandle *dh = id_to_ptr(req->fh); + fd = dirfd(dh->d); + } else { + struct handle *h = id_to_ptr(req->fh); + fd = h->fd; + } + + TRACE("[%d] %s %p(%d) is_data_sync=%d\n", handler->token, + is_dir ? "FSYNCDIR" : "FSYNC", + id_to_ptr(req->fh), fd, is_data_sync); + int res = is_data_sync ? fdatasync(fd) : fsync(fd); + if (res == -1) { return -errno; } return 0; @@ -1496,7 +1514,8 @@ static int handle_fuse_request(struct fuse *fuse, struct fuse_handler* handler, return handle_release(fuse, handler, hdr, req); } - case FUSE_FSYNC: { + case FUSE_FSYNC: + case FUSE_FSYNCDIR: { const struct fuse_fsync_in *req = data; return handle_fsync(fuse, handler, hdr, req); } @@ -1524,7 +1543,6 @@ static int handle_fuse_request(struct fuse *fuse, struct fuse_handler* handler, return handle_releasedir(fuse, handler, hdr, req); } -// case FUSE_FSYNCDIR: case FUSE_INIT: { /* init_in -> init_out */ const struct fuse_init_in *req = data; return handle_init(fuse, handler, hdr, req); @@ -1621,12 +1639,12 @@ static int read_package_list(struct fuse *fuse) { if (sscanf(buf, "%s %d %*d %*s %*s %s", package_name, &appid, gids) == 3) { char* package_name_dup = strdup(package_name); - hashmapPut(fuse->package_to_appid, package_name_dup, (void*) appid); + hashmapPut(fuse->package_to_appid, package_name_dup, (void*) (uintptr_t) appid); char* token = strtok(gids, ","); while (token != NULL) { if (strtoul(token, NULL, 10) == fuse->write_gid) { - hashmapPut(fuse->appid_with_rw, (void*) appid, (void*) 1); + hashmapPut(fuse->appid_with_rw, (void*) (uintptr_t) appid, (void*) (uintptr_t) 1); break; } token = strtok(NULL, ","); diff --git a/toolbox/Android.mk b/toolbox/Android.mk index 4fff9f5..c53f17d 100644 --- a/toolbox/Android.mk +++ b/toolbox/Android.mk @@ -2,73 +2,75 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) TOOLS := \ - ls \ - mount \ cat \ - ps \ - kill \ - ln \ - insmod \ - rmmod \ - lsmod \ - ifconfig \ - rm \ - mkdir \ - rmdir \ - getevent \ - sendevent \ - date \ - wipe \ - sync \ - umount \ - start \ - stop \ - notify \ + chcon \ + chmod \ + chown \ + clear \ cmp \ - dmesg \ - route \ - hd \ + date \ dd \ df \ + dmesg \ + du \ + getenforce \ + getevent \ getprop \ - setprop \ - watchprops \ - log \ - sleep \ - renice \ - printenv \ - smd \ - chmod \ - chown \ - newfs_msdos \ - netstat \ - ioctl \ - mv \ - schedtop \ - top \ - iftop \ + getsebool \ + hd \ id \ - uptime \ - vmstat \ - nandread \ + ifconfig \ + iftop \ + insmod \ + ioctl \ ionice \ - touch \ + kill \ + ln \ + load_policy \ + log \ + ls \ + lsmod \ lsof \ - du \ md5 \ - clear \ - getenforce \ - setenforce \ - chcon \ + mkdir \ + mknod \ + mkswap \ + mount \ + mv \ + nandread \ + netstat \ + newfs_msdos \ + nohup \ + notify \ + printenv \ + ps \ + readlink \ + renice \ restorecon \ + rm \ + rmdir \ + rmmod \ + route \ runcon \ - getsebool \ + schedtop \ + sendevent \ + setenforce \ + setprop \ setsebool \ - load_policy \ - swapon \ + sleep \ + smd \ + start \ + stop \ swapoff \ - mkswap \ - readlink + swapon \ + sync \ + top \ + touch \ + umount \ + uptime \ + vmstat \ + watchprops \ + wipe \ ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT))) TOOLS += r @@ -80,24 +82,37 @@ ALL_TOOLS += \ grep LOCAL_SRC_FILES := \ + cp/cp.c \ + cp/utils.c \ dynarray.c \ - toolbox.c \ + grep/fastgrep.c \ + grep/file.c \ + grep/grep.c \ + grep/queue.c \ + grep/util.c \ $(patsubst %,%.c,$(TOOLS)) \ - cp/cp.c cp/utils.c \ - grep/grep.c grep/fastgrep.c grep/file.c grep/queue.c grep/util.c + toolbox.c \ + uid_from_user.c \ -LOCAL_C_INCLUDES := bionic/libc/bionic +LOCAL_CFLAGS += \ + -std=gnu99 \ + -Werror -Wno-unused-parameter \ + -include bsd-compatibility.h \ -LOCAL_CFLAGS += -Wno-unused-parameter +LOCAL_C_INCLUDES += external/openssl/include LOCAL_SHARED_LIBRARIES := \ - libcutils \ - liblog \ - libc \ - libusbhost \ - libselinux + libcrypto \ + libcutils \ + libselinux \ + +# libusbhost is only used by lsusb, and that isn't usually included in toolbox. +# The linker strips out all the unused library code in the normal case. +LOCAL_STATIC_LIBRARIES := \ + libusbhost \ LOCAL_MODULE := toolbox +LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk # Including this will define $(intermediates). # diff --git a/toolbox/bsd-compatibility.h b/toolbox/bsd-compatibility.h new file mode 100644 index 0000000..a304631 --- /dev/null +++ b/toolbox/bsd-compatibility.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2014, The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/types.h> + +/* We want chown to support user.group as well as user:group. */ +#define SUPPORT_DOT + +__BEGIN_DECLS + +extern int uid_from_user(const char* name, uid_t* uid); + +__END_DECLS diff --git a/toolbox/chown.c b/toolbox/chown.c index 92efee6..6ac2233 100644 --- a/toolbox/chown.c +++ b/toolbox/chown.c @@ -1,73 +1,302 @@ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> +/* $NetBSD: chown.c,v 1.8 2012/10/24 01:12:51 enami Exp $ */ + +/* + * Copyright (c) 1988, 1993, 1994, 2003 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1988, 1993, 1994, 2003\ + The Regents of the University of California. All rights reserved."); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)chown.c 8.8 (Berkeley) 4/4/94"; +#else +__RCSID("$NetBSD: chown.c,v 1.8 2012/10/24 01:12:51 enami Exp $"); +#endif +#endif /* not lint */ + #include <sys/types.h> +#include <sys/stat.h> + +#include <ctype.h> #include <dirent.h> +#include <err.h> #include <errno.h> -#include <pwd.h> +#include <locale.h> +#include <fts.h> #include <grp.h> - +#include <pwd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> #include <unistd.h> -#include <time.h> +#include <getopt.h> + +static void a_gid(const char *); +static void a_uid(const char *); +static id_t id(const char *, const char *); +__dead static void usage(void); + +static uid_t uid; +static gid_t gid; +static int ischown; +static const char *myname; + +struct option chown_longopts[] = { + { "reference", required_argument, 0, + 1 }, + { NULL, 0, 0, + 0 }, +}; -int chown_main(int argc, char **argv) +int +chown_main(int argc, char **argv) { - int i; - - if (argc < 3) { - fprintf(stderr, "Usage: chown <USER>[:GROUP] <FILE1> [FILE2] ...\n"); - return 10; - } - - // Copy argv[1] to 'user' so we can truncate it at the period - // if a group id specified. - char user[32]; - char *group = NULL; - strncpy(user, argv[1], sizeof(user)); - if ((group = strchr(user, ':')) != NULL) { - *group++ = '\0'; - } else if ((group = strchr(user, '.')) != NULL) { - *group++ = '\0'; - } - - // Lookup uid (and gid if specified) - struct passwd *pw; - struct group *grp = NULL; - uid_t uid; - gid_t gid = -1; // passing -1 to chown preserves current group - - pw = getpwnam(user); - if (pw != NULL) { - uid = pw->pw_uid; - } else { - char* endptr; - uid = (int) strtoul(user, &endptr, 0); - if (endptr == user) { // no conversion - fprintf(stderr, "No such user '%s'\n", user); - return 10; - } - } - - if (group != NULL) { - grp = getgrnam(group); - if (grp != NULL) { - gid = grp->gr_gid; - } else { - char* endptr; - gid = (int) strtoul(group, &endptr, 0); - if (endptr == group) { // no conversion - fprintf(stderr, "No such group '%s'\n", group); - return 10; - } - } - } - - for (i = 2; i < argc; i++) { - if (chown(argv[i], uid, gid) < 0) { - fprintf(stderr, "Unable to chown %s: %s\n", argv[i], strerror(errno)); - return 10; - } - } - - return 0; + FTS *ftsp; + FTSENT *p; + int Hflag, Lflag, Rflag, ch, fflag, fts_options, hflag, rval, vflag; + char *cp, *reference; + int (*change_owner)(const char *, uid_t, gid_t); + + setprogname(*argv); + + (void)setlocale(LC_ALL, ""); + + myname = getprogname(); + ischown = (myname[2] == 'o'); + reference = NULL; + + Hflag = Lflag = Rflag = fflag = hflag = vflag = 0; + while ((ch = getopt_long(argc, argv, "HLPRfhv", + chown_longopts, NULL)) != -1) + switch (ch) { + case 1: + reference = optarg; + break; + case 'H': + Hflag = 1; + Lflag = 0; + break; + case 'L': + Lflag = 1; + Hflag = 0; + break; + case 'P': + Hflag = Lflag = 0; + break; + case 'R': + Rflag = 1; + break; + case 'f': + fflag = 1; + break; + case 'h': + /* + * In System V the -h option causes chown/chgrp to + * change the owner/group of the symbolic link. + * 4.4BSD's symbolic links didn't have owners/groups, + * so it was an undocumented noop. + * In NetBSD 1.3, lchown(2) is introduced. + */ + hflag = 1; + break; + case 'v': + vflag = 1; + break; + case '?': + default: + usage(); + } + argv += optind; + argc -= optind; + + if (argc == 0 || (argc == 1 && reference == NULL)) + usage(); + + fts_options = FTS_PHYSICAL; + if (Rflag) { + if (Hflag) + fts_options |= FTS_COMFOLLOW; + if (Lflag) { + if (hflag) + errx(EXIT_FAILURE, + "the -L and -h options " + "may not be specified together."); + fts_options &= ~FTS_PHYSICAL; + fts_options |= FTS_LOGICAL; + } + } else if (!hflag) + fts_options |= FTS_COMFOLLOW; + + uid = (uid_t)-1; + gid = (gid_t)-1; + if (reference == NULL) { + if (ischown) { + if ((cp = strchr(*argv, ':')) != NULL) { + *cp++ = '\0'; + a_gid(cp); + } +#ifdef SUPPORT_DOT + else if ((cp = strrchr(*argv, '.')) != NULL) { + if (uid_from_user(*argv, &uid) == -1) { + *cp++ = '\0'; + a_gid(cp); + } + } +#endif + a_uid(*argv); + } else + a_gid(*argv); + argv++; + } else { + struct stat st; + + if (stat(reference, &st) == -1) + err(EXIT_FAILURE, "Cannot stat `%s'", reference); + if (ischown) + uid = st.st_uid; + gid = st.st_gid; + } + + if ((ftsp = fts_open(argv, fts_options, NULL)) == NULL) + err(EXIT_FAILURE, "fts_open"); + + for (rval = EXIT_SUCCESS; (p = fts_read(ftsp)) != NULL;) { + change_owner = chown; + switch (p->fts_info) { + case FTS_D: + if (!Rflag) /* Change it at FTS_DP. */ + fts_set(ftsp, p, FTS_SKIP); + continue; + case FTS_DNR: /* Warn, chown, continue. */ + warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); + rval = EXIT_FAILURE; + break; + case FTS_ERR: /* Warn, continue. */ + case FTS_NS: + warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); + rval = EXIT_FAILURE; + continue; + case FTS_SL: /* Ignore unless -h. */ + /* + * All symlinks we found while doing a physical + * walk end up here. + */ + if (!hflag) + continue; + /* + * Note that if we follow a symlink, fts_info is + * not FTS_SL but FTS_F or whatever. And we should + * use lchown only for FTS_SL and should use chown + * for others. + */ + change_owner = lchown; + break; + case FTS_SLNONE: /* Ignore. */ + /* + * The only symlinks that end up here are ones that + * don't point to anything. Note that if we are + * doing a phisycal walk, we never reach here unless + * we asked to follow explicitly. + */ + continue; + default: + break; + } + + if ((*change_owner)(p->fts_accpath, uid, gid) && !fflag) { + warn("%s", p->fts_path); + rval = EXIT_FAILURE; + } else { + if (vflag) + printf("%s\n", p->fts_path); + } + } + if (errno) + err(EXIT_FAILURE, "fts_read"); + exit(rval); + /* NOTREACHED */ +} + +static void +a_gid(const char *s) +{ + struct group *gr; + + if (*s == '\0') /* Argument was "uid[:.]". */ + return; + gr = *s == '#' ? NULL : getgrnam(s); + if (gr == NULL) + gid = id(s, "group"); + else + gid = gr->gr_gid; + return; +} + +static void +a_uid(const char *s) +{ + if (*s == '\0') /* Argument was "[:.]gid". */ + return; + if (*s == '#' || uid_from_user(s, &uid) == -1) { + uid = id(s, "user"); + } + return; +} + +static id_t +id(const char *name, const char *type) +{ + id_t val; + char *ep; + + errno = 0; + if (*name == '#') + name++; + val = (id_t)strtoul(name, &ep, 10); + if (errno) + err(EXIT_FAILURE, "%s", name); + if (*ep != '\0') + errx(EXIT_FAILURE, "%s: invalid %s name", name, type); + return (val); +} + +static void +usage(void) +{ + + (void)fprintf(stderr, + "Usage: %s [-R [-H | -L | -P]] [-fhv] %s file ...\n" + "\t%s [-R [-H | -L | -P]] [-fhv] --reference=rfile file ...\n", + myname, ischown ? "owner:group|owner|:group" : "group", + myname); + exit(EXIT_FAILURE); } diff --git a/toolbox/cp/cp.c b/toolbox/cp/cp.c index bd3c70e..e666453 100644 --- a/toolbox/cp/cp.c +++ b/toolbox/cp/cp.c @@ -95,12 +95,14 @@ enum op { FILE_TO_FILE, FILE_TO_DIR, DIR_TO_DNE }; static int copy(char *[], enum op, int); +#ifndef ANDROID static void progress(int sig __unused) { pinfo++; } +#endif int cp_main(int argc, char *argv[]) diff --git a/toolbox/cp/utils.c b/toolbox/cp/utils.c index b682bbe..9d0390f 100644 --- a/toolbox/cp/utils.c +++ b/toolbox/cp/utils.c @@ -380,10 +380,11 @@ copy_special(struct stat *from_stat, int exists) int setfile(struct stat *fs, int fd) { - int rval, islink; + int rval = 0; +#ifndef ANDROID + int islink = S_ISLNK(fs->st_mode); +#endif - rval = 0; - islink = S_ISLNK(fs->st_mode); fs->st_mode &= S_ISUID | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO; /* @@ -401,13 +402,13 @@ setfile(struct stat *fs, int fd) fs->st_mode &= ~(S_ISUID | S_ISGID); } #ifdef ANDROID - if (fd ? fchmod(fd, fs->st_mode) : chmod(to.p_path, fs->st_mode)) { + if (fd ? fchmod(fd, fs->st_mode) : chmod(to.p_path, fs->st_mode)) { #else - if (fd ? fchmod(fd, fs->st_mode) : lchmod(to.p_path, fs->st_mode)) { + if (fd ? fchmod(fd, fs->st_mode) : lchmod(to.p_path, fs->st_mode)) { #endif - warn("chmod: %s", to.p_path); - rval = 1; - } + warn("chmod: %s", to.p_path); + rval = 1; + } #ifndef ANDROID if (!islink && !Nflag) { diff --git a/toolbox/date.c b/toolbox/date.c index d6c9052..70ce1d5 100644 --- a/toolbox/date.c +++ b/toolbox/date.c @@ -1,10 +1,13 @@ +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <inttypes.h> #include <stdio.h> #include <stdlib.h> -#include <unistd.h> -#include <fcntl.h> #include <string.h> -#include <errno.h> #include <time.h> +#include <unistd.h> + #include <linux/android_alarm.h> #include <linux/rtc.h> #include <sys/ioctl.h> @@ -108,16 +111,41 @@ static void settime(char *s) { settime_rtc_tm(&tm); } +static char *parse_time(const char *str, struct timeval *ts) { + char *s; + long fs = 0; /* fractional seconds */ + + ts->tv_sec = strtoumax(str, &s, 10); + + if (*s == '.') { + s++; + int count = 0; + + /* read up to 6 digits (microseconds) */ + while (*s && isdigit(*s)) { + if (++count < 7) { + fs = fs*10 + (*s - '0'); + } + s++; + } + + for (; count < 6; count++) { + fs *= 10; + } + } + + ts->tv_usec = fs; + return s; +} + int date_main(int argc, char *argv[]) { - int c; + int c; int res; - struct tm tm; - time_t t; - struct timeval tv; - struct timespec ts; - char strbuf[260]; - int fd; + struct tm tm; + time_t t; + struct timeval tv; + char strbuf[260]; int useutc = 0; @@ -147,7 +175,6 @@ int date_main(int argc, char *argv[]) int hasfmt = argc == optind + 1 && argv[optind][0] == '+'; if(optind == argc || hasfmt) { - char buf[2000]; time(&t); if (useutc) { gmtime_r(&t, &tm); @@ -181,7 +208,7 @@ int date_main(int argc, char *argv[]) //strptime(argv[optind], NULL, &tm); //tv.tv_sec = mktime(&tm); //tv.tv_usec = 0; - strtotimeval(argv[optind], &tv); + parse_time(argv[optind], &tv); printf("time %s -> %lu.%lu\n", argv[optind], tv.tv_sec, tv.tv_usec); res = settime_alarm_timeval(&tv); if (res < 0) diff --git a/toolbox/dd.c b/toolbox/dd.c index 6b61ffb..408a496 100644 --- a/toolbox/dd.c +++ b/toolbox/dd.c @@ -356,7 +356,7 @@ dd_in(void) ++st.in_full; /* Handle full input blocks. */ - } else if (n == in.dbsz) { + } else if (n == (int64_t)in.dbsz) { in.dbcnt += in.dbrcnt = n; ++st.in_full; @@ -521,7 +521,7 @@ dd_out(int force) outp += nw; st.bytes += nw; if (nw == n) { - if (n != out.dbsz) + if (n != (int64_t)out.dbsz) ++st.out_part; else ++st.out_full; @@ -649,8 +649,8 @@ pos_in(void) void pos_out(void) { -// struct mtop t_op; - int cnt, n; +/* struct mtop t_op; */ + int64_t cnt, n; /* * If not a tape, try seeking on the file. Seeking on a pipe is @@ -681,7 +681,7 @@ pos_out(void) } /* Read it. */ - for (cnt = 0; cnt < out.offset; ++cnt) { + for (cnt = 0; cnt < (int64_t)out.offset; ++cnt) { if ((n = read(out.fd, out.db, out.dbsz)) > 0) continue; @@ -705,8 +705,8 @@ pos_out(void) /* NOTREACHED */ } - while (cnt++ < out.offset) - if ((n = bwrite(out.fd, out.db, out.dbsz)) != out.dbsz) { + while (cnt++ < (int64_t)out.offset) + if ((n = bwrite(out.fd, out.db, out.dbsz)) != (int64_t)out.dbsz) { fprintf(stderr, "%s: cannot position " "by writing: %s\n", out.name, strerror(errno)); @@ -1153,7 +1153,7 @@ c_arg(const void *a, const void *b) ((const struct arg *)b)->name)); } -static long long strsuftoll(const char* name, const char* arg, int def, unsigned int max) +static long long strsuftoll(const char* name, const char* arg, int def, unsigned long long max) { long long result; @@ -1180,7 +1180,7 @@ static void f_count(char *arg) { - cpy_cnt = strsuftoll("block count", arg, 0, LLONG_MAX); + cpy_cnt = (uint64_t)strsuftoll("block count", arg, 0, 0xFFFFFFFFFFFFFFFFULL); if (!cpy_cnt) terminate(0); } @@ -1228,14 +1228,14 @@ static void f_seek(char *arg) { - out.offset = strsuftoll("seek blocks", arg, 0, LLONG_MAX); + out.offset = (uint64_t)strsuftoll("seek blocks", arg, 0, 0xFFFFFFFFFFFFFFFFULL); } static void f_skip(char *arg) { - in.offset = strsuftoll("skip blocks", arg, 0, LLONG_MAX); + in.offset = (uint64_t)strsuftoll("skip blocks", arg, 0, 0xFFFFFFFFFFFFFFFFULL); } static void diff --git a/toolbox/du.c b/toolbox/du.c index fc7c943..c8beba5 100644 --- a/toolbox/du.c +++ b/toolbox/du.c @@ -76,7 +76,7 @@ du_main(int argc, char *argv[]) int64_t totalblocks; int ftsoptions, listfiles; int depth; - int Hflag, Lflag, aflag, ch, cflag, dflag, gkmflag, nflag, rval, sflag; + int Hflag, Lflag, aflag, ch, cflag, dflag, gkmflag, rval, sflag; const char *noargv[2]; Hflag = Lflag = aflag = cflag = dflag = gkmflag = sflag = 0; diff --git a/toolbox/getevent.c b/toolbox/getevent.c index ed381f5..da83ec3 100644 --- a/toolbox/getevent.c +++ b/toolbox/getevent.c @@ -295,6 +295,7 @@ static int open_device(const char *device, int print_flags) { int version; int fd; + int clkid = CLOCK_MONOTONIC; struct pollfd *new_ufds; char **new_device_names; char name[80]; @@ -335,6 +336,11 @@ static int open_device(const char *device, int print_flags) idstr[0] = '\0'; } + if (ioctl(fd, EVIOCSCLOCKID, &clkid) != 0) { + fprintf(stderr, "Can't enable monotonic clock reporting: %s\n", strerror(errno)); + // a non-fatal error + } + new_ufds = realloc(ufds, sizeof(ufds[0]) * (nfds + 1)); if(new_ufds == NULL) { fprintf(stderr, "out of memory\n"); @@ -470,9 +476,9 @@ static int scan_dir(const char *dirname, int print_flags) return 0; } -static void usage(int argc, char *argv[]) +static void usage(char *name) { - fprintf(stderr, "Usage: %s [-t] [-n] [-s switchmask] [-S] [-v [mask]] [-d] [-p] [-i] [-l] [-q] [-c count] [-r] [device]\n", argv[0]); + fprintf(stderr, "Usage: %s [-t] [-n] [-s switchmask] [-S] [-v [mask]] [-d] [-p] [-i] [-l] [-q] [-c count] [-r] [device]\n", name); fprintf(stderr, " -t: show time stamps\n"); fprintf(stderr, " -n: don't print newlines\n"); fprintf(stderr, " -s: print switch states for given bits\n"); @@ -492,13 +498,11 @@ int getevent_main(int argc, char *argv[]) int c; int i; int res; - int pollres; int get_time = 0; int print_device = 0; char *newline = "\n"; uint16_t get_switch = 0; struct input_event event; - int version; int print_flags = 0; int print_flags_set = 0; int dont_block = -1; @@ -570,7 +574,7 @@ int getevent_main(int argc, char *argv[]) fprintf(stderr, "%s: invalid option -%c\n", argv[0], optopt); case 'h': - usage(argc, argv); + usage(argv[0]); exit(1); } } while (1); @@ -582,7 +586,7 @@ int getevent_main(int argc, char *argv[]) optind++; } if (optind != argc) { - usage(argc, argv); + usage(argv[0]); exit(1); } nfds = 1; @@ -629,7 +633,8 @@ int getevent_main(int argc, char *argv[]) return 0; while(1) { - pollres = poll(ufds, nfds, -1); + //int pollres = + poll(ufds, nfds, -1); //printf("poll %d, returned %d\n", nfds, pollres); if(ufds[0].revents & POLLIN) { read_notify(device_path, ufds[0].fd, print_flags); diff --git a/toolbox/getevent.h b/toolbox/getevent.h index 2b76209..0482d04 100644 --- a/toolbox/getevent.h +++ b/toolbox/getevent.h @@ -652,6 +652,7 @@ static struct label snd_labels[] = { LABEL_END, }; +#if 0 static struct label id_labels[] = { LABEL(ID_BUS), LABEL(ID_VENDOR), @@ -682,6 +683,7 @@ static struct label bus_labels[] = { LABEL(BUS_SPI), LABEL_END, }; +#endif static struct label mt_tool_labels[] = { LABEL(MT_TOOL_FINGER), diff --git a/toolbox/getprop.c b/toolbox/getprop.c index c001fda..dcc0ea0 100644 --- a/toolbox/getprop.c +++ b/toolbox/getprop.c @@ -3,7 +3,6 @@ #include <cutils/properties.h> -#include <sys/system_properties.h> #include "dynarray.h" static void record_prop(const char* key, const char* name, void* opaque) @@ -31,12 +30,8 @@ static void list_properties(void) strlist_done(list); } -int __system_property_wait(prop_info *pi); - int getprop_main(int argc, char *argv[]) { - int n = 0; - if (argc == 1) { list_properties(); } else { diff --git a/toolbox/grep/file.c b/toolbox/grep/file.c index 86b7658..d28dff5 100644 --- a/toolbox/grep/file.c +++ b/toolbox/grep/file.c @@ -78,7 +78,9 @@ static inline int grep_refill(struct file *f) { ssize_t nr; +#ifndef ANDROID int bzerr; +#endif bufpos = buffer; bufrem = 0; diff --git a/toolbox/grep/grep.c b/toolbox/grep/grep.c index b5bb2ef..7b2c487 100644 --- a/toolbox/grep/grep.c +++ b/toolbox/grep/grep.c @@ -294,10 +294,8 @@ read_patterns(const char *fn) err(2, "%s", fn); line = NULL; len = 0; -#ifndef ANDROID while ((rlen = getline(&line, &len, f)) != -1) add_pattern(line, *line == '\n' ? 0 : (size_t)rlen); -#endif free(line); if (ferror(f)) err(2, "%s", fn); @@ -405,7 +403,7 @@ grep_main(int argc, char *argv[]) Aflag = 0; else if (Aflag > LLONG_MAX / 10) { errno = ERANGE; - err(2, NULL); + err(2, "%llu", Aflag); } Aflag = Bflag = (Aflag * 10) + (c - '0'); break; @@ -422,10 +420,10 @@ grep_main(int argc, char *argv[]) l = strtoull(optarg, &ep, 10); if (((errno == ERANGE) && (l == ULLONG_MAX)) || ((errno == EINVAL) && (l == 0))) - err(2, NULL); + err(2, "strtoull"); else if (ep[0] != '\0') { errno = EINVAL; - err(2, NULL); + err(2, "empty"); } if (c == 'A') Aflag = l; @@ -511,10 +509,10 @@ grep_main(int argc, char *argv[]) mcount = strtoull(optarg, &ep, 10); if (((errno == ERANGE) && (mcount == ULLONG_MAX)) || ((errno == EINVAL) && (mcount == 0))) - err(2, NULL); + err(2, "strtoull"); else if (ep[0] != '\0') { errno = EINVAL; - err(2, NULL); + err(2, "empty"); } break; case 'n': diff --git a/toolbox/grep/util.c b/toolbox/grep/util.c index 497db06..5712fee 100644 --- a/toolbox/grep/util.c +++ b/toolbox/grep/util.c @@ -273,7 +273,7 @@ procfile(const char *fn) return (c); } -#define iswword(x) (iswalnum((x)) || (x) == L'_') +#define iswword(x) (iswalnum((wint_t)(x)) || (x) == L'_') /* * Processes a line comparing it with the specified patterns. Each pattern @@ -323,7 +323,7 @@ procline(struct str *l, int nottext) continue; /* Check for whole word match */ if (fg_pattern[i].word && pmatch.rm_so != 0) { - wint_t wbegin, wend; + wchar_t wbegin, wend; wbegin = wend = L' '; if (pmatch.rm_so != 0 && diff --git a/toolbox/hd.c b/toolbox/hd.c index 0d2f96a..7c9998e 100644 --- a/toolbox/hd.c +++ b/toolbox/hd.c @@ -14,7 +14,6 @@ int hd_main(int argc, char *argv[]) unsigned char buf[4096]; int res; int read_len; - int rv = 0; int i; int filepos = 0; int sum; diff --git a/toolbox/ifconfig.c b/toolbox/ifconfig.c index 80c0e5c..b953176 100644 --- a/toolbox/ifconfig.c +++ b/toolbox/ifconfig.c @@ -61,11 +61,11 @@ int ifconfig_main(int argc, char *argv[]) { struct ifreq ifr; int s; - unsigned int addr, mask, flags; + unsigned int flags; char astring[20]; char mstring[20]; char *updown, *brdcst, *loopbk, *ppp, *running, *multi; - + argc--; argv++; @@ -85,13 +85,17 @@ int ifconfig_main(int argc, char *argv[]) perror(ifr.ifr_name); return -1; } else - addr = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr; + strlcpy(astring, + inet_ntoa(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr), + sizeof(astring)); if (ioctl(s, SIOCGIFNETMASK, &ifr) < 0) { perror(ifr.ifr_name); return -1; } else - mask = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr; + strlcpy(mstring, + inet_ntoa(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr), + sizeof(mstring)); if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0) { perror(ifr.ifr_name); @@ -99,16 +103,6 @@ int ifconfig_main(int argc, char *argv[]) } else flags = ifr.ifr_flags; - sprintf(astring, "%d.%d.%d.%d", - addr & 0xff, - ((addr >> 8) & 0xff), - ((addr >> 16) & 0xff), - ((addr >> 24) & 0xff)); - sprintf(mstring, "%d.%d.%d.%d", - mask & 0xff, - ((mask >> 8) & 0xff), - ((mask >> 16) & 0xff), - ((mask >> 24) & 0xff)); printf("%s: ip %s mask %s flags [", ifr.ifr_name, astring, mstring diff --git a/toolbox/insmod.c b/toolbox/insmod.c index fb1448b..d252433 100644 --- a/toolbox/insmod.c +++ b/toolbox/insmod.c @@ -73,7 +73,7 @@ int insmod_main(int argc, char **argv) char *ptr = opts; for (i = 2; (i < argc) && (ptr < end); i++) { - len = MIN(strlen(argv[i]), end - ptr); + len = MIN(strlen(argv[i]), (size_t)(end - ptr)); memcpy(ptr, argv[i], len); ptr += len; *ptr++ = ' '; diff --git a/toolbox/ioctl.c b/toolbox/ioctl.c index fb555d2..17fabff 100644 --- a/toolbox/ioctl.c +++ b/toolbox/ioctl.c @@ -21,9 +21,9 @@ int ioctl_main(int argc, char *argv[]) int arg_size = 4; int direct_arg = 0; uint32_t ioctl_nr; - void *ioctl_args; + void *ioctl_args = NULL; uint8_t *ioctl_argp; - uint8_t *ioctl_argp_save; + uint8_t *ioctl_argp_save = NULL; int rem; do { @@ -112,6 +112,7 @@ int ioctl_main(int argc, char *argv[]) else res = ioctl(fd, ioctl_nr, 0); if (res < 0) { + free(ioctl_args); fprintf(stderr, "ioctl 0x%x failed, %d\n", ioctl_nr, res); return 1; } @@ -124,5 +125,6 @@ int ioctl_main(int argc, char *argv[]) } printf("\n"); } + free(ioctl_args); return 0; } diff --git a/toolbox/load_policy.c b/toolbox/load_policy.c index eb5aba6..90d48c4 100644 --- a/toolbox/load_policy.c +++ b/toolbox/load_policy.c @@ -10,7 +10,7 @@ int load_policy_main(int argc, char **argv) { - int fd, rc, vers; + int fd, rc; struct stat sb; void *map; const char *path; diff --git a/toolbox/ls.c b/toolbox/ls.c index 3cc5bb2..011f7b5 100644 --- a/toolbox/ls.c +++ b/toolbox/ls.c @@ -137,7 +137,7 @@ static int listfile_size(const char *path, const char *filename, struct stat *s, /* blocks are 512 bytes, we want output to be KB */ if ((flags & LIST_SIZE) != 0) { - printf("%lld ", s->st_blocks / 2); + printf("%lld ", (long long)s->st_blocks / 2); } if ((flags & LIST_CLASSIFY) != 0) { @@ -205,7 +205,7 @@ static int listfile_long(const char *path, struct stat *s, int flags) break; case S_IFREG: printf("%s %-8s %-8s %8lld %s %s\n", - mode, user, group, s->st_size, date, name); + mode, user, group, (long long)s->st_size, date, name); break; case S_IFLNK: { char linkto[256]; @@ -321,7 +321,7 @@ static int listfile(const char *dirname, const char *filename, int flags) } if(flags & LIST_INODE) { - printf("%8llu ", s.st_ino); + printf("%8llu ", (unsigned long long)s.st_ino); } if ((flags & LIST_MACLABEL) != 0) { @@ -443,7 +443,6 @@ static int listpath(const char *name, int flags) int ls_main(int argc, char **argv) { int flags = 0; - int listed = 0; if(argc > 1) { int i; diff --git a/toolbox/lsof.c b/toolbox/lsof.c index 113c120..bee981d 100644 --- a/toolbox/lsof.c +++ b/toolbox/lsof.c @@ -99,10 +99,7 @@ out: static void print_maps(struct pid_info_t* info) { FILE *maps; - char buffer[PATH_MAX + 100]; - size_t offset; - int major, minor; char device[10]; long int inode; char file[PATH_MAX]; @@ -113,7 +110,7 @@ static void print_maps(struct pid_info_t* info) if (!maps) goto out; - while (fscanf(maps, "%*x-%*x %*s %zx %5s %ld %s\n", &offset, device, &inode, + while (fscanf(maps, "%*x-%*x %*s %zx %s %ld %s\n", &offset, device, &inode, file) == 4) { // We don't care about non-file maps if (inode == 0 || !strcmp(device, "00:00")) diff --git a/toolbox/md5.c b/toolbox/md5.c index 2fb8b05..5de4d9e 100644 --- a/toolbox/md5.c +++ b/toolbox/md5.c @@ -4,12 +4,7 @@ #include <unistd.h> #include <sys/stat.h> #include <sys/types.h> -#include <md5.h> - -/* When this was written, bionic's md5.h did not define this. */ -#ifndef MD5_DIGEST_LENGTH -#define MD5_DIGEST_LENGTH 16 -#endif +#include <openssl/md5.h> static int usage() { @@ -30,7 +25,6 @@ static int do_md5(const char *path) return -1; } - /* Note that bionic's MD5_* functions return void. */ MD5_Init(&md5_ctx); while (1) { diff --git a/toolbox/mkdir.c b/toolbox/mkdir.c index 656970a..398d350 100644 --- a/toolbox/mkdir.c +++ b/toolbox/mkdir.c @@ -15,7 +15,6 @@ static int usage() int mkdir_main(int argc, char *argv[]) { - int symbolic = 0; int ret; if(argc < 2 || strcmp(argv[1], "--help") == 0) { return usage(); diff --git a/toolbox/mknod.c b/toolbox/mknod.c new file mode 100644 index 0000000..0fedece --- /dev/null +++ b/toolbox/mknod.c @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2014, The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/stat.h> + +static int print_usage() { + fprintf(stderr, "mknod <path> [b|c|u|p] <major> <minor>\n"); + return EXIT_FAILURE; +} + +int mknod_main(int argc, char **argv) { + char *path = NULL; + int major = 0; + int minor = 0; + int args = 0; + mode_t mode = 0660; + + /* Check correct argument count is 3 or 5 */ + if (argc != 3 && argc != 5) { + fprintf(stderr, "Incorrect argument count\n"); + return print_usage(); + } + + path = argv[1]; + + const char node_type = *argv[2]; + switch (node_type) { + case 'b': + mode |= S_IFBLK; + args = 5; + break; + case 'c': + case 'u': + mode |= S_IFCHR; + args = 5; + break; + case 'p': + mode |= S_IFIFO; + args = 3; + break; + default: + fprintf(stderr, "Invalid node type '%c'\n", node_type); + return print_usage(); + } + + if (argc != args) { + if (args == 5) { + fprintf(stderr, "Node type '%c' requires <major> and <minor>\n", node_type); + } else { + fprintf(stderr, "Node type '%c' does not require <major> and <minor>\n", node_type); + } + return print_usage(); + } + + if (args == 5) { + major = atoi(argv[3]); + minor = atoi(argv[4]); + } + + if (mknod(path, mode, makedev(major, minor))) { + perror("Unable to create node"); + return EXIT_FAILURE; + } + return 0; +} diff --git a/toolbox/mkswap.c b/toolbox/mkswap.c index 1710ef6..0904152 100644 --- a/toolbox/mkswap.c +++ b/toolbox/mkswap.c @@ -1,6 +1,5 @@ #include <stdio.h> #include <unistd.h> -#include <asm/page.h> #include <sys/swap.h> #include <sys/types.h> #include <sys/stat.h> diff --git a/toolbox/nandread.c b/toolbox/nandread.c index d43b2fe..bd19942 100644 --- a/toolbox/nandread.c +++ b/toolbox/nandread.c @@ -1,9 +1,10 @@ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> #include <ctype.h> #include <errno.h> #include <fcntl.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> #include <unistd.h> #include <mtd/mtd-user.h> @@ -177,11 +178,7 @@ int nandread_main(int argc, char **argv) if (rawmode) { rawmode = mtdinfo.oobsize; -#if !defined(MTD_STUPID_LOCK) /* using uapi kernel headers */ ret = ioctl(fd, MTDFILEMODE, MTD_FILE_MODE_RAW); -#else /* still using old kernel headers */ - ret = ioctl(fd, MTDFILEMODE, MTD_MODE_RAW); -#endif if (ret) { fprintf(stderr, "failed set raw mode for %s, %s\n", devname, strerror(errno)); @@ -193,18 +190,18 @@ int nandread_main(int argc, char **argv) for (pos = start, opos = 0; pos < end; pos += mtdinfo.writesize) { bad_block = 0; if (verbose > 3) - printf("reading at %llx\n", pos); + printf("reading at %" PRIx64 "\n", pos); lseek64(fd, pos, SEEK_SET); ret = read(fd, buffer, mtdinfo.writesize + rawmode); if (ret < (int)mtdinfo.writesize) { - fprintf(stderr, "short read at %llx, %d\n", pos, ret); + fprintf(stderr, "short read at %" PRIx64 ", %d\n", pos, ret); bad_block = 2; } if (!rawmode) { oobbuf.start = pos; ret = ioctl(fd, MEMREADOOB, &oobbuf); if (ret) { - fprintf(stderr, "failed to read oob data at %llx, %d\n", pos, ret); + fprintf(stderr, "failed to read oob data at %" PRIx64 ", %d\n", pos, ret); bad_block = 2; } } @@ -217,17 +214,17 @@ int nandread_main(int argc, char **argv) bpos = pos / mtdinfo.erasesize * mtdinfo.erasesize; ret = ioctl(fd, MEMGETBADBLOCK, &bpos); if (ret && errno != EOPNOTSUPP) { - printf("badblock at %llx\n", pos); + printf("badblock at %" PRIx64 "\n", pos); bad_block = 1; } if (ecc.corrected != last_ecc.corrected) - printf("ecc corrected, %u, at %llx\n", ecc.corrected - last_ecc.corrected, pos); + printf("ecc corrected, %u, at %" PRIx64 "\n", ecc.corrected - last_ecc.corrected, pos); if (ecc.failed != last_ecc.failed) - printf("ecc failed, %u, at %llx\n", ecc.failed - last_ecc.failed, pos); + printf("ecc failed, %u, at %" PRIx64 "\n", ecc.failed - last_ecc.failed, pos); if (ecc.badblocks != last_ecc.badblocks) - printf("ecc badblocks, %u, at %llx\n", ecc.badblocks - last_ecc.badblocks, pos); + printf("ecc badblocks, %u, at %" PRIx64 "\n", ecc.badblocks - last_ecc.badblocks, pos); if (ecc.bbtblocks != last_ecc.bbtblocks) - printf("ecc bbtblocks, %u, at %llx\n", ecc.bbtblocks - last_ecc.bbtblocks, pos); + printf("ecc bbtblocks, %u, at %" PRIx64 "\n", ecc.bbtblocks - last_ecc.bbtblocks, pos); if (!rawmode) { oob_fixed = (uint8_t *)oob_data; @@ -245,18 +242,18 @@ int nandread_main(int argc, char **argv) if (outfd >= 0) { ret = write(outfd, buffer, mtdinfo.writesize + spare_size); if (ret < (int)(mtdinfo.writesize + spare_size)) { - fprintf(stderr, "short write at %llx, %d\n", pos, ret); + fprintf(stderr, "short write at %" PRIx64 ", %d\n", pos, ret); close(outfd); outfd = -1; } if (ecc.corrected != last_ecc.corrected) - fprintf(statusfile, "%08llx: ecc corrected\n", opos); + fprintf(statusfile, "%08" PRIx64 ": ecc corrected\n", opos); if (ecc.failed != last_ecc.failed) - fprintf(statusfile, "%08llx: ecc failed\n", opos); + fprintf(statusfile, "%08" PRIx64 ": ecc failed\n", opos); if (bad_block == 1) - fprintf(statusfile, "%08llx: badblock\n", opos); + fprintf(statusfile, "%08" PRIx64 ": badblock\n", opos); if (bad_block == 2) - fprintf(statusfile, "%08llx: read error\n", opos); + fprintf(statusfile, "%08" PRIx64 ": read error\n", opos); opos += mtdinfo.writesize + spare_size; } @@ -265,7 +262,7 @@ int nandread_main(int argc, char **argv) if (test_empty(buffer, mtdinfo.writesize + mtdinfo.oobsize + spare_size)) empty_pages++; else if (verbose > 2 || (verbose > 1 && !(pos & (mtdinfo.erasesize - 1)))) - printf("page at %llx (%d oobbytes): %08x %08x %08x %08x " + printf("page at %" PRIx64 " (%d oobbytes): %08x %08x %08x %08x " "%08x %08x %08x %08x\n", pos, oobbuf.start, oob_data[0], oob_data[1], oob_data[2], oob_data[3], oob_data[4], oob_data[5], oob_data[6], oob_data[7]); diff --git a/toolbox/newfs_msdos.c b/toolbox/newfs_msdos.c index 27dca42..30b9f77 100644 --- a/toolbox/newfs_msdos.c +++ b/toolbox/newfs_msdos.c @@ -360,7 +360,7 @@ newfs_msdos_main(int argc, char *argv[]) if (!opt_create && !strchr(fname, '/')) { snprintf(buf, sizeof(buf), "%s%s", _PATH_DEV, fname); if (!(fname = strdup(buf))) - err(1, NULL); + err(1, "%s", buf); } dtype = *argv; if (opt_create) { @@ -493,7 +493,7 @@ newfs_msdos_main(int argc, char *argv[]) if (!strchr(bname, '/')) { snprintf(buf, sizeof(buf), "/boot/%s", bname); if (!(bname = strdup(buf))) - err(1, NULL); + err(1, "%s", buf); } if ((fd1 = open(bname, O_RDONLY)) == -1 || fstat(fd1, &sb)) err(1, "%s", bname); @@ -611,7 +611,7 @@ newfs_msdos_main(int argc, char *argv[]) now = tv.tv_sec; tm = localtime(&now); if (!(img = malloc(bpb.bps))) - err(1, NULL); + err(1, "%u", bpb.bps); dir = bpb.res + (bpb.spf ? bpb.spf : bpb.bspf) * bpb.nft; for (lsn = 0; lsn < dir + (fat == 32 ? bpb.spc : rds); lsn++) { x = lsn; @@ -728,14 +728,14 @@ newfs_msdos_main(int argc, char *argv[]) static void check_mounted(const char *fname, mode_t mode) { +#ifdef ANDROID + warnx("Skipping mount checks"); +#else struct statfs *mp; const char *s1, *s2; size_t len; int n, r; -#ifdef ANDROID - warnx("Skipping mount checks"); -#else if (!(n = getmntinfo(&mp, MNT_NOWAIT))) err(1, "getmntinfo"); len = strlen(_PATH_DEV); diff --git a/toolbox/nohup.c b/toolbox/nohup.c new file mode 100644 index 0000000..363999d --- /dev/null +++ b/toolbox/nohup.c @@ -0,0 +1,26 @@ +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +int nohup_main(int argc, char *argv[]) +{ + if (argc < 2) { + fprintf(stderr, "Usage: %s [-n] program args...\n", argv[0]); + return EXIT_FAILURE; + } + signal(SIGHUP, SIG_IGN); + argv++; + if (strcmp(argv[0], "-n") == 0) { + argv++; + signal(SIGINT, SIG_IGN); + signal(SIGSTOP, SIG_IGN); + signal(SIGTTIN, SIG_IGN); + signal(SIGTTOU, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + signal(SIGTERM, SIG_IGN); + } + execvp(argv[0], argv); + perror(argv[0]); + return EXIT_FAILURE; +} diff --git a/toolbox/ps.c b/toolbox/ps.c index 7c35ccb..57b4280 100644 --- a/toolbox/ps.c +++ b/toolbox/ps.c @@ -28,9 +28,12 @@ static char *nexttok(char **strp) #define SHOW_POLICY 4 #define SHOW_CPU 8 #define SHOW_MACLABEL 16 +#define SHOW_ABI 32 static int display_flags = 0; +static void print_exe_abi(int pid); + static int ps_line(int pid, int tid, char *namefilter) { char statline[1024]; @@ -40,7 +43,7 @@ static int ps_line(int pid, int tid, char *namefilter) struct stat stats; int fd, r; char *ptr, *name, *state; - int ppid, tty; + int ppid; unsigned wchan, rss, vss, eip; unsigned utime, stime; int prio, nice, rtprio, sched, psr; @@ -88,7 +91,7 @@ static int ps_line(int pid, int tid, char *namefilter) ppid = atoi(nexttok(&ptr)); nexttok(&ptr); // pgrp nexttok(&ptr); // sid - tty = atoi(nexttok(&ptr)); + nexttok(&ptr); // tty nexttok(&ptr); // tpgid nexttok(&ptr); // flags @@ -130,7 +133,7 @@ static int ps_line(int pid, int tid, char *namefilter) rtprio = atoi(nexttok(&ptr)); // rt_priority sched = atoi(nexttok(&ptr)); // scheduling policy - tty = atoi(nexttok(&ptr)); + nexttok(&ptr); // tty if(tid != 0) { ppid = pid; @@ -170,7 +173,11 @@ static int ps_line(int pid, int tid, char *namefilter) else printf(" %.2s ", get_sched_policy_name(p)); } - printf(" %08x %08x %s %s", wchan, eip, state, cmdline[0] ? cmdline : name); + printf(" %08x %08x %s ", wchan, eip, state); + if (display_flags & SHOW_ABI) { + print_exe_abi(pid); + } + printf("%s", cmdline[0] ? cmdline : name); if(display_flags&SHOW_TIME) printf(" (u:%d, s:%d)", utime, stime); @@ -179,6 +186,39 @@ static int ps_line(int pid, int tid, char *namefilter) return 0; } +static void print_exe_abi(int pid) +{ + int fd, r; + char exeline[1024]; + + sprintf(exeline, "/proc/%d/exe", pid); + fd = open(exeline, O_RDONLY); + if(fd == 0) { + printf(" "); + return; + } + r = read(fd, exeline, 5 /* 4 byte ELFMAG + 1 byte EI_CLASS */); + close(fd); + if(r < 0) { + printf(" "); + return; + } + if (memcmp("\177ELF", exeline, 4) != 0) { + printf("?? "); + return; + } + switch (exeline[4]) { + case 1: + printf("32 "); + return; + case 2: + printf("64 "); + return; + default: + printf("?? "); + return; + } +} void ps_threads(int pid, char *namefilter) { @@ -224,7 +264,9 @@ int ps_main(int argc, char **argv) display_flags |= SHOW_PRIO; } else if(!strcmp(argv[1],"-c")) { display_flags |= SHOW_CPU; - } else if(isdigit(argv[1][0])){ + } else if(!strcmp(argv[1],"--abi")) { + display_flags |= SHOW_ABI; + } else if(isdigit(argv[1][0])){ pidfilter = atoi(argv[1]); } else { namefilter = argv[1]; @@ -236,10 +278,11 @@ int ps_main(int argc, char **argv) if (display_flags & SHOW_MACLABEL) { printf("LABEL USER PID PPID NAME\n"); } else { - printf("USER PID PPID VSIZE RSS %s%s %s WCHAN PC NAME\n", + printf("USER PID PPID VSIZE RSS %s%s %s WCHAN PC %sNAME\n", (display_flags&SHOW_CPU)?"CPU ":"", (display_flags&SHOW_PRIO)?"PRIO NICE RTPRI SCHED ":"", - (display_flags&SHOW_POLICY)?"PCY " : ""); + (display_flags&SHOW_POLICY)?"PCY " : "", + (display_flags&SHOW_ABI)?"ABI " : ""); } while((de = readdir(d)) != 0){ if(isdigit(de->d_name[0])){ diff --git a/toolbox/restorecon.c b/toolbox/restorecon.c index f9f604f..3568625 100644 --- a/toolbox/restorecon.c +++ b/toolbox/restorecon.c @@ -2,76 +2,44 @@ #include <stdio.h> #include <stdlib.h> #include <errno.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fts.h> #include <selinux/selinux.h> -#include <selinux/label.h> #include <selinux/android.h> -static struct selabel_handle *sehandle; static const char *progname; -static int nochange; -static int verbose; static void usage(void) { - fprintf(stderr, "usage: %s [-nrRv] pathname...\n", progname); + fprintf(stderr, "usage: %s [-DFnrRv] pathname...\n", progname); exit(1); } -static int restore(const char *pathname, const struct stat *sb) -{ - char *oldcontext, *newcontext; - - if (lgetfilecon(pathname, &oldcontext) < 0) { - fprintf(stderr, "Could not get context of %s: %s\n", - pathname, strerror(errno)); - return -1; - } - if (selabel_lookup(sehandle, &newcontext, pathname, sb->st_mode) < 0) { - fprintf(stderr, "Could not lookup context for %s: %s\n", pathname, - strerror(errno)); - return -1; - } - if (strcmp(newcontext, "<<none>>") && - strcmp(oldcontext, newcontext)) { - if (verbose) - printf("Relabeling %s from %s to %s.\n", pathname, oldcontext, newcontext); - if (!nochange) { - if (lsetfilecon(pathname, newcontext) < 0) { - fprintf(stderr, "Could not label %s with %s: %s\n", - pathname, newcontext, strerror(errno)); - return -1; - } - } - } - freecon(oldcontext); - freecon(newcontext); - return 0; -} - int restorecon_main(int argc, char **argv) { - int ch, recurse = 0, ftsflags = FTS_PHYSICAL; - int i = 0; + int ch, i, rc; + unsigned int flags = 0; progname = argv[0]; do { - ch = getopt(argc, argv, "nrRv"); + ch = getopt(argc, argv, "DFnrRv"); if (ch == EOF) break; switch (ch) { + case 'D': + flags |= SELINUX_ANDROID_RESTORECON_DATADATA; + break; + case 'F': + flags |= SELINUX_ANDROID_RESTORECON_FORCE; + break; case 'n': - nochange = 1; + flags |= SELINUX_ANDROID_RESTORECON_NOCHANGE; break; case 'r': case 'R': - recurse = 1; + flags |= SELINUX_ANDROID_RESTORECON_RECURSE; break; case 'v': - verbose = 1; + flags |= SELINUX_ANDROID_RESTORECON_VERBOSE; break; default: usage(); @@ -83,53 +51,11 @@ int restorecon_main(int argc, char **argv) if (!argc) usage(); - sehandle = selinux_android_file_context_handle(); - - if (!sehandle) { - fprintf(stderr, "Could not load file_contexts: %s\n", - strerror(errno)); - return -1; - } - - if (recurse) { - FTS *fts; - FTSENT *ftsent; - fts = fts_open(argv, ftsflags, NULL); - if (!fts) { - fprintf(stderr, "Could not traverse filesystems (first was %s): %s\n", - argv[0], strerror(errno)); - return -1; - } - while ((ftsent = fts_read(fts))) { - switch (ftsent->fts_info) { - case FTS_DP: - break; - case FTS_DNR: - case FTS_ERR: - case FTS_NS: - fprintf(stderr, "Could not access %s: %s\n", ftsent->fts_path, - strerror(errno)); - fts_set(fts, ftsent, FTS_SKIP); - break; - default: - if (restore(ftsent->fts_path, ftsent->fts_statp) < 0) - fts_set(fts, ftsent, FTS_SKIP); - break; - } - } - } else { - int i, rc; - struct stat sb; - - for (i = 0; i < argc; i++) { - rc = lstat(argv[i], &sb); - if (rc < 0) { - fprintf(stderr, "Could not stat %s: %s\n", argv[i], - strerror(errno)); - continue; - } - restore(argv[i], &sb); - } + for (i = 0; i < argc; i++) { + rc = selinux_android_restorecon(argv[i], flags); + if (rc < 0) + fprintf(stderr, "Could not restorecon %s: %s\n", argv[i], + strerror(errno)); } return 0; diff --git a/toolbox/rmdir.c b/toolbox/rmdir.c index 06f3df2..749fec8 100644 --- a/toolbox/rmdir.c +++ b/toolbox/rmdir.c @@ -11,7 +11,6 @@ static int usage() int rmdir_main(int argc, char *argv[]) { - int symbolic = 0; int ret; if(argc < 2) return usage(); diff --git a/toolbox/schedtop.c b/toolbox/schedtop.c index a76f968..2fccd2e 100644 --- a/toolbox/schedtop.c +++ b/toolbox/schedtop.c @@ -1,26 +1,23 @@ -#include <stdio.h> -#include <stdlib.h> #include <ctype.h> +#include <dirent.h> #include <fcntl.h> -#include <unistd.h> - +#include <pwd.h> +#include <signal.h> +#include <stdio.h> #include <stdint.h> +#include <stdlib.h> #include <string.h> - #include <sys/stat.h> #include <sys/types.h> -#include <dirent.h> -#include <signal.h> - -#include <pwd.h> +#include <unistd.h> struct thread_info { int pid; int tid; char name[64]; - uint64_t exec_time; - uint64_t delay_time; - uint32_t run_count; + unsigned long long exec_time; + unsigned long long delay_time; + unsigned long long run_count; }; struct thread_table { @@ -110,7 +107,8 @@ static void add_thread(int pid, int tid, struct thread_info *proc_info) sprintf(line, "/proc/%d/schedstat", pid); if (read_line(line, sizeof(line))) return; - if(sscanf(line, "%llu %llu %u", &info->exec_time, &info->delay_time, &info->run_count) != 3) + if(sscanf(line, "%llu %llu %llu", + &info->exec_time, &info->delay_time, &info->run_count) != 3) return; if (proc_info) { proc_info->exec_time += info->exec_time; @@ -183,7 +181,7 @@ static void print_threads(int pid, uint32_t flags) if (j == threads.active) printf(" %5u died\n", tid); else if (!(flags & FLAG_HIDE_IDLE) || threads.data[j].run_count - last_threads.data[i].run_count) - printf(" %5u %2u.%0*u %2u.%0*u %5u %5u.%0*u %5u.%0*u %7u %s\n", tid, + printf(" %5u %2u.%0*u %2u.%0*u %5llu %5u.%0*u %5u.%0*u %7llu %s\n", tid, NS_TO_S_D(threads.data[j].exec_time - last_threads.data[i].exec_time), NS_TO_S_D(threads.data[j].delay_time - last_threads.data[i].delay_time), threads.data[j].run_count - last_threads.data[i].run_count, @@ -229,14 +227,13 @@ static void update_table(DIR *d, uint32_t flags) } for (i = 0; i < last_processes.active; i++) { int pid = last_processes.data[i].pid; - int tid = last_processes.data[i].tid; for (j = 0; j < processes.active; j++) if (pid == processes.data[j].pid) break; if (j == processes.active) printf("%5u died\n", pid); else if (!(flags & FLAG_HIDE_IDLE) || processes.data[j].run_count - last_processes.data[i].run_count) { - printf("%5u %2u.%0*u %2u.%0*u %5u %5u.%0*u %5u.%0*u %7u %s\n", pid, + printf("%5u %2u.%0*u %2u.%0*u %5llu %5u.%0*u %5u.%0*u %7llu %s\n", pid, NS_TO_S_D(processes.data[j].exec_time - last_processes.data[i].exec_time), NS_TO_S_D(processes.data[j].delay_time - last_processes.data[i].delay_time), processes.data[j].run_count - last_processes.data[i].run_count, @@ -272,9 +269,6 @@ int schedtop_main(int argc, char **argv) { int c; DIR *d; - struct dirent *de; - char *namefilter = 0; - int pidfilter = 0; uint32_t flags = 0; int delay = 3000000; float delay_f; diff --git a/toolbox/sendevent.c b/toolbox/sendevent.c index 1608e6c..9b813f6 100644 --- a/toolbox/sendevent.c +++ b/toolbox/sendevent.c @@ -47,9 +47,8 @@ struct input_event { int sendevent_main(int argc, char *argv[]) { - int i; int fd; - int ret; + ssize_t ret; int version; struct input_event event; @@ -72,7 +71,7 @@ int sendevent_main(int argc, char *argv[]) event.code = atoi(argv[3]); event.value = atoi(argv[4]); ret = write(fd, &event, sizeof(event)); - if(ret < sizeof(event)) { + if(ret < (ssize_t) sizeof(event)) { fprintf(stderr, "write event failed, %s\n", strerror(errno)); return -1; } diff --git a/toolbox/start.c b/toolbox/start.c index 665a941..0941e64 100644 --- a/toolbox/start.c +++ b/toolbox/start.c @@ -7,14 +7,13 @@ int start_main(int argc, char *argv[]) { - char buf[1024]; - if(argc > 1) { property_set("ctl.start", argv[1]); } else { /* defaults to starting the common services stopped by stop.c */ property_set("ctl.start", "surfaceflinger"); property_set("ctl.start", "zygote"); + property_set("ctl.start", "zygote_secondary"); } return 0; diff --git a/toolbox/stop.c b/toolbox/stop.c index 460f377..ed9a293 100644 --- a/toolbox/stop.c +++ b/toolbox/stop.c @@ -5,12 +5,11 @@ int stop_main(int argc, char *argv[]) { - char buf[1024]; - if(argc > 1) { property_set("ctl.stop", argv[1]); } else{ /* defaults to stopping the common services */ + property_set("ctl.stop", "zygote_secondary"); property_set("ctl.stop", "zygote"); property_set("ctl.stop", "surfaceflinger"); } diff --git a/toolbox/swapoff.c b/toolbox/swapoff.c index 8f14158..d8f6a00 100644 --- a/toolbox/swapoff.c +++ b/toolbox/swapoff.c @@ -1,6 +1,5 @@ #include <stdio.h> #include <unistd.h> -#include <asm/page.h> #include <sys/swap.h> int swapoff_main(int argc, char **argv) diff --git a/toolbox/swapon.c b/toolbox/swapon.c index a810b3d..21d2287 100644 --- a/toolbox/swapon.c +++ b/toolbox/swapon.c @@ -2,7 +2,6 @@ #include <stdlib.h> #include <unistd.h> #include <getopt.h> -#include <asm/page.h> #include <sys/swap.h> void usage(char *name) diff --git a/toolbox/syren.c b/toolbox/syren.c index 06e329e..47c2460 100644 --- a/toolbox/syren.c +++ b/toolbox/syren.c @@ -123,7 +123,11 @@ syren_main(int argc, char **argv) r = find_reg(argv[2]); if (r == NULL) { - strcpy(name, argv[2]); + if(strlen(argv[2]) >= sizeof(name)){ + fprintf(stderr, "REGNAME too long\n"); + return 0; + } + strlcpy(name, argv[2], sizeof(name)); char *addr_str = strchr(argv[2], ':'); if (addr_str == NULL) return usage(); @@ -131,7 +135,7 @@ syren_main(int argc, char **argv) sio.page = strtoul(argv[2], 0, 0); sio.addr = strtoul(addr_str, 0, 0); } else { - strcpy(name, r->name); + strlcpy(name, r->name, sizeof(name)); sio.page = r->page; sio.addr = r->addr; } diff --git a/toolbox/top.c b/toolbox/top.c index 7642522..280a032 100644 --- a/toolbox/top.c +++ b/toolbox/top.c @@ -9,7 +9,7 @@ * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the + * the documentation and/or other materials provided with the * distribution. * * Neither the name of Google, Inc. nor the names of its contributors * may be used to endorse or promote products derived from this @@ -22,7 +22,7 @@ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF @@ -108,16 +108,20 @@ static int proc_thr_cmp(const void *a, const void *b); static int numcmp(long long a, long long b); static void usage(char *cmd); -int top_main(int argc, char *argv[]) { - int i; +static void exit_top(int signal) { + exit(EXIT_FAILURE); +} +int top_main(int argc, char *argv[]) { num_used_procs = num_free_procs = 0; + signal(SIGPIPE, exit_top); + max_procs = 0; delay = 3; iterations = -1; proc_cmp = &proc_cpu_cmp; - for (i = 1; i < argc; i++) { + for (int i = 1; i < argc; i++) { if (!strcmp(argv[i], "-m")) { if (i + 1 >= argc) { fprintf(stderr, "Option -m expects an argument.\n"); @@ -249,9 +253,9 @@ static void read_procs(void) { continue; pid = atoi(pid_dir->d_name); - + struct proc_info cur_proc; - + if (!threads) { proc = alloc_proc(); @@ -275,7 +279,7 @@ static void read_procs(void) { sprintf(filename, "/proc/%d/status", pid); read_status(filename, &cur_proc); - + proc = NULL; } @@ -310,7 +314,7 @@ static void read_procs(void) { } closedir(task_dir); - + if (!threads) add_proc(proc_num++, proc); } @@ -324,7 +328,6 @@ static void read_procs(void) { static int read_stat(char *filename, struct proc_info *proc) { FILE *file; char buf[MAX_LINE], *open_paren, *close_paren; - int res, idx; file = fopen(filename, "r"); if (!file) return 1; @@ -339,7 +342,7 @@ static int read_stat(char *filename, struct proc_info *proc) { *open_paren = *close_paren = '\0'; strncpy(proc->tname, open_paren + 1, THREAD_NAME_LEN); proc->tname[THREAD_NAME_LEN-1] = 0; - + /* Scan rest of string. */ sscanf(close_paren + 1, " %c %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d " "%lu %lu %*d %*d %*d %*d %*d %*d %*d %lu %ld " @@ -410,9 +413,7 @@ static void print_procs(void) { struct proc_info *old_proc, *proc; long unsigned total_delta_time; struct passwd *user; - struct group *group; char *user_str, user_buf[20]; - char *group_str, group_buf[20]; for (i = 0; i < num_new_procs; i++) { if (new_procs[i]) { @@ -452,7 +453,7 @@ static void print_procs(void) { new_cpu.sirqtime - old_cpu.sirqtime, total_delta_time); printf("\n"); - if (!threads) + if (!threads) printf("%5s %2s %4s %1s %5s %7s %7s %3s %-8s %s\n", "PID", "PR", "CPU%", "S", "#THR", "VSS", "RSS", "PCY", "UID", "Name"); else printf("%5s %5s %2s %4s %1s %7s %7s %3s %-8s %-15s %s\n", "PID", "TID", "PR", "CPU%", "S", "VSS", "RSS", "PCY", "UID", "Thread", "Proc"); @@ -463,20 +464,13 @@ static void print_procs(void) { if (!proc || (max_procs && (i >= max_procs))) break; user = getpwuid(proc->uid); - group = getgrgid(proc->gid); if (user && user->pw_name) { user_str = user->pw_name; } else { snprintf(user_buf, 20, "%d", proc->uid); user_str = user_buf; } - if (group && group->gr_name) { - group_str = group->gr_name; - } else { - snprintf(group_buf, 20, "%d", proc->gid); - group_str = group_buf; - } - if (!threads) + if (!threads) printf("%5d %2d %3ld%% %c %5d %6ldK %6ldK %3s %-8.8s %s\n", proc->pid, proc->prs, proc->delta_time * 100 / total_delta_time, proc->state, proc->num_threads, proc->vss / 1024, proc->rss * getpagesize() / 1024, proc->policy, user_str, proc->name[0] != 0 ? proc->name : proc->tname); else diff --git a/toolbox/uid_from_user.c b/toolbox/uid_from_user.c new file mode 100644 index 0000000..fd48d3c --- /dev/null +++ b/toolbox/uid_from_user.c @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2014, The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <pwd.h> +#include <sys/types.h> + +int uid_from_user(const char* name, uid_t* uid) { + struct passwd* pw = getpwnam(name); + if (pw == NULL) { + return -1; + } + *uid = pw->pw_uid; + return 0; +} diff --git a/toolbox/umount.c b/toolbox/umount.c index 890e870..3e17396 100644 --- a/toolbox/umount.c +++ b/toolbox/umount.c @@ -33,7 +33,6 @@ static int is_loop_mount(const char* path, char *loopdev) char mount_path[256]; char rest[256]; int result = 0; - int path_length = strlen(path); f = fopen("/proc/mounts", "r"); if (!f) { diff --git a/toolbox/watchprops.c b/toolbox/watchprops.c index bf82882..0d05aba 100644 --- a/toolbox/watchprops.c +++ b/toolbox/watchprops.c @@ -6,8 +6,6 @@ #include <cutils/properties.h> #include <cutils/hashmap.h> -#include <sys/atomics.h> - #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_ #include <sys/_system_properties.h> @@ -23,9 +21,9 @@ static bool str_equals(void *keyA, void *keyB) static void announce(char *name, char *value) { - char *x; + unsigned char *x; - for(x = value; *x; x++) { + for(x = (unsigned char *)value; *x; x++) { if((*x < 32) || (*x > 127)) *x = '.'; } @@ -77,9 +75,7 @@ static void update_watchlist(const prop_info *pi, void *cookie) int watchprops_main(int argc, char *argv[]) { - unsigned serial = 0; - unsigned count = 0; - unsigned n; + unsigned serial; Hashmap *watchlist = hashmapCreate(1024, str_hash, str_equals); if (!watchlist) @@ -87,7 +83,7 @@ int watchprops_main(int argc, char *argv[]) __system_property_foreach(populate_watchlist, watchlist); - for(;;) { + for(serial = 0;;) { serial = __system_property_wait_any(serial); __system_property_foreach(update_watchlist, watchlist); } |