Возможно ли ведение журнала событий Android Systrace непосредственно из собственного кода без JNI?
Система ведения журналов systrace для Android является фантастической, но она работает только в части кода Java, благодаря Trace.beginSection()
а также Trace.endSection()
, В C/C++ NDK (нативной) части кода его можно использовать только через JNI, который медлен или недоступен в потоках без среды Java...
Есть ли способ добавить события в основной буфер трассировки systrace или даже создать отдельный журнал из собственного кода C?
Этот старый вопрос упоминает atrace/ftrace как внутреннюю систему, которую использует systrace для Android. Может ли это быть использовано (легко)?
Бонусный поворот: поскольку отслеживание вызовов часто происходит в разделах, критичных к производительности, в идеале должна быть возможность выполнять вызовы после фактического времени события. т.е. я бы, например, предпочел бы иметь возможность указывать время для входа, а не для опросов для него самих. Но это было бы просто глазурью на торте.
3 ответа
Я не думаю, что это выставлено NDK.
Если вы посмотрите на источники, то увидите, что класс android.os.Trace вызывает собственный код для выполнения реальной работы. Этот код вызывает atrace_begin()
а также atrace_end()
, которые объявлены в заголовке в библиотеке cutils.
Вы можете использовать функции atrace напрямую, если извлекаете заголовки из полного дерева исходных текстов и связываетесь с внутренними библиотеками. Однако из заголовка видно, что atrace_begin()
это просто:
static inline void atrace_begin(uint64_t tag, const char* name)
{
if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
char buf[ATRACE_MESSAGE_LENGTH];
size_t len;
len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "B|%d|%s", getpid(), name);
write(atrace_marker_fd, buf, len);
}
}
События записываются непосредственно в дескриптор файла трассировки. (Обратите внимание, что отметка времени не является частью события; она добавляется автоматически.) Вы можете сделать что-то подобное в своем коде; увидеть atrace_init_once()
в.c файле, чтобы увидеть, как файл открывается.
Имейте в виду, что если Atrace не будет опубликован как часть NDK, любой код, использующий его, будет непереносимым и, скорее всего, выйдет из строя в прошлых или будущих версиях Android. Однако, поскольку systrace - это инструмент отладки, а не то, что вы на самом деле хотите включить в приложении, совместимость, вероятно, не является проблемой.
Публикация последующего ответа с некоторым кодом, основанным на указателях Фаддена. Пожалуйста, прочитайте его / ее ответ сначала для обзора.
Все, что для этого нужно - это написать правильно отформатированные строки в /sys/kernel/debug/tracing/trace_marker
, который можно открыть без проблем. Ниже приведен минимальный код, основанный на заголовке cutils и C-файле. Я предпочел повторно реализовать его, а не извлекать какие-либо зависимости, поэтому, если вас очень волнует правильность, проверьте строгую реализацию и / или добавьте свои собственные дополнительные проверки и обработку ошибок.
Это было проверено для работы на Android 4.4.2.
Сначала необходимо открыть файл трассировки, сохранив дескриптор файла в atrace_marker_fd
Глобальный:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define ATRACE_MESSAGE_LEN 256
int atrace_marker_fd = -1;
void trace_init()
{
atrace_marker_fd = open("/sys/kernel/debug/tracing/trace_marker", O_WRONLY);
if (atrace_marker_fd == -1) { /* do error handling */ }
}
Обычные "вложенные" трассировки, такие как Java Trace.beginSection
а также Trace.endSection
получены с:
inline void trace_begin(const char *name)
{
char buf[ATRACE_MESSAGE_LEN];
int len = snprintf(buf, ATRACE_MESSAGE_LEN, "B|%d|%s", getpid(), name);
write(atrace_marker_fd, buf, len);
}
inline void trace_end()
{
char c = 'E';
write(atrace_marker_fd, &c, 1);
}
Доступны еще два типа трассировки, которые, насколько я знаю, недоступны для Java: счетчики трасс и асинхронные трассы.
Счетчики отслеживают значение целого числа и рисуют маленький график в выводе systrace HTML. Очень полезный материал:
inline void trace_counter(const char *name, const int value)
{
char buf[ATRACE_MESSAGE_LEN];
int len = snprintf(buf, ATRACE_MESSAGE_LEN, "C|%d|%s|%i", getpid(), name, value);
write(atrace_marker_fd, buf, len);
}
Асинхронные трассировки создают не вложенные (то есть просто перекрывающиеся) интервалы. Они отображаются в виде серых сегментов над тонкой строкой состояния потока в выводе systrace HTML. Они принимают дополнительный 32-битный целочисленный аргумент, который "различает одновременные события". При завершении трассировки должны использоваться одно и то же имя и целое число:
inline void trace_async_begin(const char *name, const int32_t cookie)
{
char buf[ATRACE_MESSAGE_LEN];
int len = snprintf(buf, ATRACE_MESSAGE_LEN, "S|%d|%s|%i", getpid(), name, cookie);
write(atrace_marker_fd, buf, len);
}
inline void trace_async_end(const char *name, const int32_t cookie)
{
char buf[ATRACE_MESSAGE_LEN];
int len = snprintf(buf, ATRACE_MESSAGE_LEN, "F|%d|%s|%i", getpid(), name, cookie);
write(atrace_marker_fd, buf, len);
}
Наконец, действительно, кажется, нет способа указать время для входа в систему, за исключением перекомпиляции Android, так что это ничего не делает для "бонусного поворота".
Для тех, кто будет искать этот вопрос в будущем.
Собственные события трассировки поддерживаются, начиная с уровня API 23, ознакомьтесь с документацией здесь https://developer.android.com/topic/performance/tracing/custom-events-native .