/*
* Copyright 2015 The Chromium Authors
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "tools/android/memtrack_helper/memtrack_helper.h"
#include <dlfcn.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
/*
* This is a helper daemon for Android which makes memtrack graphics information
* accessible via a UNIX socket. It is used by telemetry and memory-infra.
* More description in the design-doc: https://goo.gl/4Y30p9 .
*/
static const char kShutdownRequest = 'Q';
// See $ANDROID/system/core/include/memtrack/memtrack.h.
typedef void* memtrack_proc_handle;
typedef int (*memtrack_init_t)(void);
typedef memtrack_proc_handle (*memtrack_proc_new_t)(void);
typedef void (*memtrack_proc_destroy_t)(memtrack_proc_handle);
typedef int (*memtrack_proc_get_t)(memtrack_proc_handle, pid_t);
typedef ssize_t (*memtrack_proc_graphics_total_t)(memtrack_proc_handle);
typedef ssize_t (*memtrack_proc_graphics_pss_t)(memtrack_proc_handle);
typedef ssize_t (*memtrack_proc_gl_total_t)(memtrack_proc_handle);
typedef ssize_t (*memtrack_proc_gl_pss_t)(memtrack_proc_handle);
typedef ssize_t (*memtrack_proc_other_total_t)(memtrack_proc_handle);
typedef ssize_t (*memtrack_proc_other_pss_t)(memtrack_proc_handle);
static memtrack_init_t memtrack_init;
static memtrack_proc_new_t memtrack_proc_new;
static memtrack_proc_destroy_t memtrack_proc_destroy;
static memtrack_proc_get_t memtrack_proc_get;
static memtrack_proc_graphics_total_t memtrack_proc_graphics_total;
static memtrack_proc_graphics_pss_t memtrack_proc_graphics_pss;
static memtrack_proc_gl_total_t memtrack_proc_gl_total;
static memtrack_proc_gl_pss_t memtrack_proc_gl_pss;
static memtrack_proc_other_total_t memtrack_proc_other_total;
static memtrack_proc_other_pss_t memtrack_proc_other_pss;
static void send_response(int client_sock, const char* resp) {
send(client_sock, resp, strlen(resp) + 1, 0);
}
static void send_shutdown_request(struct sockaddr_un* addr) {
int sock = socket(AF_UNIX, SOCK_SEQPACKET, 0);
connect(sock, (struct sockaddr*)addr, sizeof(*addr));
send(sock, &kShutdownRequest, 1, 0);
close(sock);
}
static void handle_one_request(int client_sock) {
char buf[32];
char response[4096] = "";
ssize_t rsize = recv(client_sock, buf, sizeof(buf) - 1, 0);
if (rsize < 1)
return;
buf[rsize] = '\0';
if (buf[0] == kShutdownRequest)
exit(EXIT_SUCCESS);
pid_t pid = -1;
if (sscanf(buf, "%d", &pid) != 1 || pid < 0)
return send_response(client_sock, "ERR invalid pid");
memtrack_proc_handle handle = memtrack_proc_new();
if (!handle)
return send_response(client_sock, "ERR memtrack_proc_new()");
if (memtrack_proc_get(handle, pid)) {
memtrack_proc_destroy(handle);
return send_response(client_sock, "ERR memtrack_proc_get()");
}
char* response_ptr = &response[0];
if (memtrack_proc_graphics_total) {
response_ptr += sprintf(response_ptr, "graphics_total %zd\n",
memtrack_proc_graphics_total(handle));
}
if (memtrack_proc_graphics_pss) {
response_ptr += sprintf(response_ptr, "graphics_pss %zd\n",
memtrack_proc_graphics_pss(handle));
}
if (memtrack_proc_gl_total) {
response_ptr += sprintf(response_ptr, "gl_total %zd\n",
memtrack_proc_gl_total(handle));
}
if (memtrack_proc_gl_pss) {
response_ptr += sprintf(response_ptr, "gl_pss %zd\n",
memtrack_proc_gl_pss(handle));
}
if (memtrack_proc_other_total) {
response_ptr += sprintf(response_ptr, "other_total %zd\n",
memtrack_proc_other_total(handle));
}
if (memtrack_proc_other_pss) {
response_ptr += sprintf(response_ptr, "other_pss %zd\n",
memtrack_proc_other_pss(handle));
}
memtrack_proc_destroy(handle);
send_response(client_sock, response);
}
static void daemonize() {
pid_t pid;
pid = fork();
if (pid < 0)
exit_with_failure("fork");
if (pid > 0) {
// Main process keeps TTY while intermediate child do daemonization
// because adb can immediately kill a process disconnected from adb's TTY.
int ignore;
wait(&ignore);
exit(EXIT_SUCCESS);
}
if (setsid() == -1)
exit_with_failure("setsid");
chdir("/");
umask(0);
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
open("/dev/null", O_RDONLY);
open("/dev/null", O_WRONLY);
open("/dev/null", O_RDWR);
pid = fork();
if (pid < 0)
exit_with_failure("fork");
if (pid > 0)
exit(EXIT_SUCCESS);
}
int main(int argc, char** argv) {
int res;
if (getuid() != 0) {
fprintf(stderr, "FATAL: %s must be run as root!\n", argv[0]);
return EXIT_FAILURE;
}
void* const libhandle = dlopen("libmemtrack.so", RTLD_GLOBAL | RTLD_NOW);
if (!libhandle)
exit_with_failure("dlopen() libmemtrack.so");
memtrack_init = (memtrack_init_t)dlsym(libhandle, "memtrack_init");
memtrack_proc_new =
(memtrack_proc_new_t)dlsym(libhandle, "memtrack_proc_new");
memtrack_proc_destroy =
(memtrack_proc_destroy_t)dlsym(libhandle, "memtrack_proc_destroy");
memtrack_proc_get =
(memtrack_proc_get_t)dlsym(libhandle, "memtrack_proc_get");
memtrack_proc_graphics_total = (memtrack_proc_graphics_total_t)dlsym(
libhandle, "memtrack_proc_graphics_total");
memtrack_proc_graphics_pss = (memtrack_proc_graphics_pss_t)dlsym(
libhandle, "memtrack_proc_graphics_pss");
memtrack_proc_gl_total =
(memtrack_proc_gl_total_t)dlsym(libhandle, "memtrack_proc_gl_total");
memtrack_proc_gl_pss =
(memtrack_proc_gl_pss_t)dlsym(libhandle, "memtrack_proc_gl_pss");
memtrack_proc_other_total = (memtrack_proc_other_total_t)dlsym(
libhandle, "memtrack_proc_other_total");
memtrack_proc_other_pss =
(memtrack_proc_other_pss_t)dlsym(libhandle, "memtrack_proc_other_pss");
if (!memtrack_proc_new || !memtrack_proc_destroy || !memtrack_proc_get) {
exit_with_failure("dlsym() libmemtrack.so");
}
const int server_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
if (server_fd < 0)
exit_with_failure("socket");
/* Initialize the socket */
struct sockaddr_un server_addr;
init_memtrack_server_addr(&server_addr);
/* Shutdown previously running instances if any. */
int i;
for (i = 0; i < 3; ++i) {
res = bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr));
if (res && errno == EADDRINUSE) {
send_shutdown_request(&server_addr);
usleep(250000);
continue;
}
break;
}
if (res)
exit_with_failure("bind");
if (argc > 1 && strcmp(argv[1], "-d") == 0)
daemonize();
long pid = getpid();
fprintf(stderr, "pid=%ld\n", pid);
__android_log_print(ANDROID_LOG_INFO, kLogTag, "pid=%ld\n", pid);
if (memtrack_init) {
res = memtrack_init();
if (res == -ENOENT) {
exit_with_failure("Unable to load memtrack module in libhardware. "
"Probably implementation is missing in this ROM.");
} else if (res != 0) {
exit_with_failure("memtrack_init() returned non-zero status.");
}
}
if (listen(server_fd, 128 /* max number of queued requests */))
exit_with_failure("listen");
for (;;) {
struct sockaddr_un client_addr;
socklen_t len = sizeof(client_addr);
int client_sock = accept(server_fd, (struct sockaddr*)&client_addr, &len);
handle_one_request(client_sock);
close(client_sock);
}
return EXIT_SUCCESS;
}