e4030384c4
When Android is running in a container, ftrace trace_marker may be disabled, e.g. Chrome OS verified mode. To enable trace, a socket is created for everyone to send trace event. Modify libcutils so apps using it can write trace to the socket. To achieve this goal without touching code for normal device, trace-container.c is created for code specific to container. Shared code between trace-dev.c and trace-container.c is moved to local trace-dev.h. Note that to avoid sharing FDs across different processes, Zygote closes FD and reopen it to /dev/null if it is a socket. Therefore, we need to manually close FD and reopen it again after forking. This is done in the atrace_set_tracing_enabled as Zygote also use it to clear flags during forking. Related change: - http://crrev.com/2400163003 (Chromium) - http://ag/2028075 (Android system/core) - http://ag/1738271 (Android device/google/cheets2) Bug: 29776474, 62410809 Test: run chrome://tracing and see data from the host side. Change-Id: I3fe969e284b972c28e4f32f23cd948aaea61a043
173 lines
5.7 KiB
C++
173 lines
5.7 KiB
C++
/*
|
|
* Copyright (C) 2017 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 __TRACE_DEV_INC
|
|
#define __TRACE_DEV_INC
|
|
|
|
#define LOG_TAG "cutils-trace"
|
|
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <limits.h>
|
|
#include <pthread.h>
|
|
#include <stdatomic.h>
|
|
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
|
|
#include <cutils/compiler.h>
|
|
#include <cutils/properties.h>
|
|
#include <cutils/trace.h>
|
|
#include <log/log.h>
|
|
#include <log/log_properties.h>
|
|
|
|
/**
|
|
* Maximum size of a message that can be logged to the trace buffer.
|
|
* Note this message includes a tag, the pid, and the string given as the name.
|
|
* Names should be kept short to get the most use of the trace buffer.
|
|
*/
|
|
#define ATRACE_MESSAGE_LENGTH 1024
|
|
|
|
atomic_bool atrace_is_ready = ATOMIC_VAR_INIT(false);
|
|
int atrace_marker_fd = -1;
|
|
uint64_t atrace_enabled_tags = ATRACE_TAG_NOT_READY;
|
|
static bool atrace_is_debuggable = false;
|
|
static atomic_bool atrace_is_enabled = ATOMIC_VAR_INIT(true);
|
|
static pthread_mutex_t atrace_tags_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
// Set whether this process is debuggable, which determines whether
|
|
// application-level tracing is allowed when the ro.debuggable system property
|
|
// is not set to '1'.
|
|
void atrace_set_debuggable(bool debuggable)
|
|
{
|
|
atrace_is_debuggable = debuggable;
|
|
atrace_update_tags();
|
|
}
|
|
|
|
// Check whether the given command line matches one of the comma-separated
|
|
// values listed in the app_cmdlines property.
|
|
static bool atrace_is_cmdline_match(const char* cmdline)
|
|
{
|
|
int count = property_get_int32("debug.atrace.app_number", 0);
|
|
|
|
char buf[PROPERTY_KEY_MAX];
|
|
char value[PROPERTY_VALUE_MAX];
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
snprintf(buf, sizeof(buf), "debug.atrace.app_%d", i);
|
|
property_get(buf, value, "");
|
|
if (strcmp(value, cmdline) == 0) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Determine whether application-level tracing is enabled for this process.
|
|
static bool atrace_is_app_tracing_enabled()
|
|
{
|
|
bool sys_debuggable = __android_log_is_debuggable();
|
|
bool result = false;
|
|
|
|
if (sys_debuggable || atrace_is_debuggable) {
|
|
// Check whether tracing is enabled for this process.
|
|
FILE * file = fopen("/proc/self/cmdline", "re");
|
|
if (file) {
|
|
char cmdline[4096];
|
|
if (fgets(cmdline, sizeof(cmdline), file)) {
|
|
result = atrace_is_cmdline_match(cmdline);
|
|
} else {
|
|
ALOGE("Error reading cmdline: %s (%d)", strerror(errno), errno);
|
|
}
|
|
fclose(file);
|
|
} else {
|
|
ALOGE("Error opening /proc/self/cmdline: %s (%d)", strerror(errno),
|
|
errno);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// Read the sysprop and return the value tags should be set to
|
|
static uint64_t atrace_get_property()
|
|
{
|
|
char value[PROPERTY_VALUE_MAX];
|
|
char *endptr;
|
|
uint64_t tags;
|
|
|
|
property_get("debug.atrace.tags.enableflags", value, "0");
|
|
errno = 0;
|
|
tags = strtoull(value, &endptr, 0);
|
|
if (value[0] == '\0' || *endptr != '\0') {
|
|
ALOGE("Error parsing trace property: Not a number: %s", value);
|
|
return 0;
|
|
} else if (errno == ERANGE || tags == ULLONG_MAX) {
|
|
ALOGE("Error parsing trace property: Number too large: %s", value);
|
|
return 0;
|
|
}
|
|
|
|
// Only set the "app" tag if this process was selected for app-level debug
|
|
// tracing.
|
|
if (atrace_is_app_tracing_enabled()) {
|
|
tags |= ATRACE_TAG_APP;
|
|
} else {
|
|
tags &= ~ATRACE_TAG_APP;
|
|
}
|
|
|
|
return (tags | ATRACE_TAG_ALWAYS) & ATRACE_TAG_VALID_MASK;
|
|
}
|
|
|
|
// Update tags if tracing is ready. Useful as a sysprop change callback.
|
|
void atrace_update_tags()
|
|
{
|
|
uint64_t tags;
|
|
if (CC_UNLIKELY(atomic_load_explicit(&atrace_is_ready, memory_order_acquire))) {
|
|
if (atomic_load_explicit(&atrace_is_enabled, memory_order_acquire)) {
|
|
tags = atrace_get_property();
|
|
pthread_mutex_lock(&atrace_tags_mutex);
|
|
atrace_enabled_tags = tags;
|
|
pthread_mutex_unlock(&atrace_tags_mutex);
|
|
} else {
|
|
// Tracing is disabled for this process, so we simply don't
|
|
// initialize the tags.
|
|
pthread_mutex_lock(&atrace_tags_mutex);
|
|
atrace_enabled_tags = ATRACE_TAG_NOT_READY;
|
|
pthread_mutex_unlock(&atrace_tags_mutex);
|
|
}
|
|
}
|
|
}
|
|
|
|
#define WRITE_MSG(format_begin, format_end, name, value) { \
|
|
char buf[ATRACE_MESSAGE_LENGTH]; \
|
|
int pid = getpid(); \
|
|
int len = snprintf(buf, sizeof(buf), format_begin "%s" format_end, pid, \
|
|
name, value); \
|
|
if (len >= (int) sizeof(buf)) { \
|
|
/* Given the sizeof(buf), and all of the current format buffers, \
|
|
* it is impossible for name_len to be < 0 if len >= sizeof(buf). */ \
|
|
int name_len = strlen(name) - (len - sizeof(buf)) - 1; \
|
|
/* Truncate the name to make the message fit. */ \
|
|
ALOGW("Truncated name in %s: %s\n", __FUNCTION__, name); \
|
|
len = snprintf(buf, sizeof(buf), format_begin "%.*s" format_end, pid, \
|
|
name_len, name, value); \
|
|
} \
|
|
write(atrace_marker_fd, buf, len); \
|
|
}
|
|
|
|
#endif // __TRACE_DEV_INC
|