mirror of
https://github.com/openharmony/developtools_bytrace.git
synced 2026-07-18 16:24:29 -04:00
9383a94e3f
Signed-off-by: xiahaiqin <xiahaiqin1@huawei.com> Change-Id: Id035dd3d2f4b6647e2ea02c5b0a7489aa7a1a450
959 lines
33 KiB
C++
959 lines
33 KiB
C++
/*
|
|
* Copyright (C) 2021 Huawei Device Co., Ltd.
|
|
* 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 <cinttypes>
|
|
#include <csignal>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <ctime>
|
|
#include <fstream>
|
|
#include <regex>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <fcntl.h>
|
|
#include <getopt.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
#include <zlib.h>
|
|
#include "hitrace_meter.h"
|
|
#include "bytrace_osal.h"
|
|
#include "securec.h"
|
|
|
|
using namespace std;
|
|
using namespace OHOS::Developtools::BytraceOsal;
|
|
|
|
namespace {
|
|
struct option g_longOptions[] = {
|
|
{ "buffer_size", required_argument, nullptr, 0 },
|
|
{ "trace_clock", required_argument, nullptr, 0 },
|
|
{ "help", no_argument, nullptr, 0 },
|
|
{ "output", required_argument, nullptr, 0 },
|
|
{ "time", required_argument, nullptr, 0 },
|
|
{ "trace_begin", no_argument, nullptr, 0 },
|
|
{ "trace_finish", no_argument, nullptr, 0 },
|
|
{ "trace_dump", no_argument, nullptr, 0 },
|
|
{ "list_categories", no_argument, nullptr, 0 },
|
|
{ "overwrite", no_argument, nullptr, 0 },
|
|
};
|
|
const unsigned int CHUNK_SIZE = 65536;
|
|
const int BLOCK_SIZE = 4096;
|
|
|
|
const string TRACE_TAG_PROPERTY = "debug.hitrace.tags.enableflags";
|
|
|
|
// various operating paths of ftrace
|
|
const string TRACING_ON_PATH = "tracing_on";
|
|
const string TRACE_PATH = "trace";
|
|
const string TRACE_MARKER_PATH = "trace_marker";
|
|
const string BUFFER_SIZE_PATH = "buffer_size_kb";
|
|
const string CURRENT_TRACER_PATH = "current_tracer";
|
|
const string TRACE_CLOCK_PATH = "trace_clock";
|
|
const string OVER_WRITE_PATH = "options/overwrite";
|
|
const string RECORD_TGID_PATH = "options/record-tgid";
|
|
|
|
// support customization of some parameters
|
|
|
|
const int MIN_BUFFER_SIZE = 256;
|
|
const int MAX_BUFFER_SIZE = 307200; // 300 MB
|
|
constexpr unsigned int MAX_OUTPUT_LEN = 255;
|
|
const int PAGE_SIZE_KB = 4; // 4 KB
|
|
int g_traceDuration = 5;
|
|
int g_bufferSizeKB = 2048;
|
|
string g_clock = "boot";
|
|
bool g_overwrite = true;
|
|
string g_outputFile;
|
|
bool g_compress = false;
|
|
|
|
string g_traceRootPath;
|
|
|
|
const unsigned int START_NONE = 0;
|
|
const unsigned int START_NORMAL = 1;
|
|
const unsigned int START_ASYNC = 2;
|
|
unsigned int g_traceStart = START_NORMAL;
|
|
bool g_traceStop = true;
|
|
bool g_traceDump = true;
|
|
|
|
map<string, TagCategory> g_tagMap;
|
|
vector<uint64_t> g_userEnabledTags;
|
|
vector<string> g_kernelEnabledPaths;
|
|
}
|
|
|
|
static bool IsTraceMounted()
|
|
{
|
|
const string debugfsPath = "/sys/kernel/debug/tracing/";
|
|
const string tracefsPath = "/sys/kernel/tracing/";
|
|
|
|
if (access((debugfsPath + TRACE_MARKER_PATH).c_str(), F_OK) != -1) {
|
|
g_traceRootPath = debugfsPath;
|
|
return true;
|
|
}
|
|
if (access((tracefsPath + TRACE_MARKER_PATH).c_str(), F_OK) != -1) {
|
|
g_traceRootPath = tracefsPath;
|
|
return true;
|
|
}
|
|
|
|
(void)fprintf(stderr, "Error: Did not find trace folder\n");
|
|
return false;
|
|
}
|
|
|
|
static bool IsFileExit(const string& filename)
|
|
{
|
|
return access((g_traceRootPath + filename).c_str(), F_OK) != -1;
|
|
}
|
|
|
|
static bool IsWritableFile(const string& filename)
|
|
{
|
|
return access((g_traceRootPath + filename).c_str(), W_OK) != -1;
|
|
}
|
|
|
|
static bool WriteStrToFile(const string& filename, const std::string& str)
|
|
{
|
|
ofstream out;
|
|
out.open(g_traceRootPath + filename, ios::out);
|
|
if (out.fail()) {
|
|
fprintf(stderr, "Error: Did not open %s\n", filename.c_str());
|
|
return false;
|
|
}
|
|
out << str;
|
|
if (out.bad()) {
|
|
fprintf(stderr, "Error: Did not write %s\n", filename.c_str());
|
|
out.close();
|
|
return false;
|
|
}
|
|
out.close();
|
|
return true;
|
|
}
|
|
|
|
static bool SetFtraceEnabled(const string& path, bool enabled)
|
|
{
|
|
return WriteStrToFile(path, enabled ? "1" : "0");
|
|
}
|
|
|
|
static bool IsTagSupported(const string& name)
|
|
{
|
|
auto it = g_tagMap.find(name);
|
|
if (it == g_tagMap.end()) {
|
|
return false;
|
|
}
|
|
|
|
TagCategory tagCategory = it->second;
|
|
if (tagCategory.type != KERNEL) {
|
|
g_userEnabledTags.push_back(tagCategory.tag);
|
|
return true;
|
|
}
|
|
|
|
bool findPath = false;
|
|
for (int i = 0; i < MAX_SYS_FILES; i++) {
|
|
const string path = tagCategory.sysfiles[i].path;
|
|
if (path.size() == 0) {
|
|
continue;
|
|
}
|
|
if (IsWritableFile(path)) {
|
|
g_kernelEnabledPaths.push_back(path);
|
|
findPath = true;
|
|
} else if (IsFileExit(path)) {
|
|
fprintf(stderr, "Warning: category \"%s\" requires root "
|
|
"privileges.\n", name.c_str());
|
|
}
|
|
}
|
|
return findPath;
|
|
}
|
|
|
|
static string CanonicalizeSpecPath(const char* src)
|
|
{
|
|
if (src == nullptr || strlen(src) >= PATH_MAX) {
|
|
fprintf(stderr, "Error: CanonicalizeSpecPath %s failed", src);
|
|
return "";
|
|
}
|
|
char resolvedPath[PATH_MAX] = { 0 };
|
|
#if defined(_WIN32)
|
|
if (!_fullpath(resolvedPath, src, PATH_MAX)) {
|
|
fprintf(stderr, "Error: _fullpath %s failed\n", src);
|
|
return "";
|
|
}
|
|
#else
|
|
if (access(src, F_OK) == 0) {
|
|
if (realpath(src, resolvedPath) == nullptr) {
|
|
fprintf(stderr, "Error: realpath %s failed", src);
|
|
return "";
|
|
}
|
|
} else {
|
|
string fileName(src);
|
|
if (fileName.find("..") == string::npos) {
|
|
if (sprintf_s(resolvedPath, PATH_MAX, "%s", src) == -1) {
|
|
fprintf(stderr, "Error: sprintf_s %s failed", src);
|
|
return "";
|
|
}
|
|
} else {
|
|
fprintf(stderr, "Error: find .. %s failed", src);
|
|
return "";
|
|
}
|
|
}
|
|
#endif
|
|
string res(resolvedPath);
|
|
return res;
|
|
}
|
|
|
|
static string ReadFile(const string& filename)
|
|
{
|
|
string resolvedPath = CanonicalizeSpecPath((g_traceRootPath + filename).c_str());
|
|
ifstream fin(resolvedPath.c_str());
|
|
if (!fin.is_open()) {
|
|
fprintf(stderr, "open file: %s failed!\n", (g_traceRootPath + filename).c_str());
|
|
return "";
|
|
}
|
|
|
|
string str((istreambuf_iterator<char>(fin)), istreambuf_iterator<char>());
|
|
fin.close();
|
|
return str;
|
|
}
|
|
|
|
static bool SetBufferSize(int bufferSize)
|
|
{
|
|
if (!WriteStrToFile(CURRENT_TRACER_PATH, "nop")) {
|
|
fprintf(stderr, "Error: write \"nop\" to %s\n", CURRENT_TRACER_PATH.c_str());
|
|
}
|
|
return WriteStrToFile(BUFFER_SIZE_PATH, to_string(bufferSize));
|
|
}
|
|
|
|
static bool SetClock(const string& timeclock)
|
|
{
|
|
string allClocks = ReadFile(TRACE_CLOCK_PATH);
|
|
size_t begin = allClocks.find("[");
|
|
size_t end = allClocks.find("]");
|
|
string newClock;
|
|
if (begin != string::npos && end != string::npos &&
|
|
timeclock.compare(0, timeclock.size(), allClocks, begin + 1, end - begin - 1) >= 0) {
|
|
return true;
|
|
} else if (allClocks.find(timeclock) != string::npos) {
|
|
newClock = timeclock;
|
|
} else if (allClocks.find("boot") != string::npos) {
|
|
// boot: This is the boot clock (CLOCK_BOOTTIME) and is based on the fast monotonic clock,
|
|
// but also accounts for time in suspend.
|
|
newClock = "boot";
|
|
} else if (allClocks.find("mono") != string::npos) {
|
|
// mono: uses the fast monotonic clock (CLOCK_MONOTONIC)
|
|
// which is monotonic and is subject to NTP rate adjustments.
|
|
newClock = "mono";
|
|
} else if (allClocks.find("global") != string::npos) {
|
|
// global: is in sync with all CPUs but may be a bit slower than the local clock.
|
|
newClock = "global";
|
|
} else {
|
|
fprintf(stderr, "You can set trace clock in %s\n", allClocks.c_str());
|
|
return false;
|
|
}
|
|
if (newClock.size() != 0) {
|
|
return WriteStrToFile(TRACE_CLOCK_PATH, newClock);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool SetOverWriteEnable(bool enabled)
|
|
{
|
|
return SetFtraceEnabled(OVER_WRITE_PATH, enabled);
|
|
}
|
|
|
|
static bool SetTgidEnable(bool enabled)
|
|
{
|
|
return SetFtraceEnabled(RECORD_TGID_PATH, enabled);
|
|
}
|
|
|
|
static bool DisableAllFtraceEvents()
|
|
{
|
|
bool isTrue = true;
|
|
for (auto it = g_tagMap.begin(); it != g_tagMap.end(); ++it) {
|
|
TagCategory tag = it->second;
|
|
if (tag.type != KERNEL) {
|
|
continue;
|
|
}
|
|
for (int i = 0; i < MAX_SYS_FILES; i++) {
|
|
const string path = tag.sysfiles[i].path;
|
|
if ((path.size() > 0) && IsWritableFile(path)) {
|
|
isTrue = isTrue && SetFtraceEnabled(path, false);
|
|
}
|
|
}
|
|
}
|
|
return isTrue;
|
|
}
|
|
|
|
static bool SetProperty(const string& property, const string& value)
|
|
{
|
|
return SetPropertyInner(property, value);
|
|
}
|
|
|
|
static bool SetTraceTagsEnabled(uint64_t tags)
|
|
{
|
|
string value = to_string(tags);
|
|
return SetProperty(TRACE_TAG_PROPERTY, value);
|
|
}
|
|
|
|
static bool RefreshServices()
|
|
{
|
|
bool res = false;
|
|
|
|
res = RefreshBinderServices();
|
|
if (!res) {
|
|
return res;
|
|
}
|
|
res = RefreshHalServices();
|
|
return res;
|
|
}
|
|
|
|
static bool SetUserSpaceSettings()
|
|
{
|
|
uint64_t enabledTags = 0;
|
|
for (auto tag: g_userEnabledTags) {
|
|
enabledTags |= tag;
|
|
}
|
|
return SetTraceTagsEnabled(enabledTags) && RefreshServices();
|
|
}
|
|
|
|
static bool ClearUserSpaceSettings()
|
|
{
|
|
return SetTraceTagsEnabled(0) && RefreshServices();
|
|
}
|
|
|
|
static bool SetKernelSpaceSettings()
|
|
{
|
|
bool isTrue = SetBufferSize(g_bufferSizeKB) && SetClock(g_clock) &&
|
|
SetOverWriteEnable(g_overwrite) && DisableAllFtraceEvents();
|
|
for (const auto& path : g_kernelEnabledPaths) {
|
|
SetFtraceEnabled(path, true);
|
|
}
|
|
return isTrue;
|
|
}
|
|
|
|
static bool ClearKernelSpaceSettings()
|
|
{
|
|
return DisableAllFtraceEvents() && SetOverWriteEnable(true) && SetBufferSize(1) && SetClock("boot");
|
|
}
|
|
|
|
static bool SetViewStyle()
|
|
{
|
|
return SetTgidEnable(true);
|
|
}
|
|
|
|
static void ShowListCategory()
|
|
{
|
|
printf(" %18s description:\n", "tagName:");
|
|
for (auto it = g_tagMap.begin(); it != g_tagMap.end(); ++it) {
|
|
string key = it->first;
|
|
TagCategory tag = it->second;
|
|
if (IsTagSupported(key)) {
|
|
printf(" %18s - %s\n", tag.name.c_str(), tag.description.c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ShowHelp(const string& cmd)
|
|
{
|
|
printf("usage: %s [options] [categories...]\n", cmd.c_str());
|
|
printf("options include:\n"
|
|
" -b N Sets the size of the buffer (KB) for storing and reading traces. The default \n"
|
|
" buffer size is 2048 KB.\n"
|
|
" --buffer_size N Like \"-b N\".\n"
|
|
" -l Lists available bytrace categories.\n"
|
|
" --list_categories Like \"-l\".\n"
|
|
" -t N Sets the bytrace running duration in seconds (5s by default), which depends on \n"
|
|
" the time required for analysis.\n"
|
|
" --time N Like \"-t N\".\n"
|
|
" --trace_clock clock\n"
|
|
" Sets the type of the clock for adding a timestamp to a trace, which can be\n"
|
|
" boot (default), global, mono, uptime, or perf.\n"
|
|
" --trace_begin Starts capturing traces.\n"
|
|
" --trace_dump Dumps traces to a specified path (stdout by default).\n"
|
|
" --trace_finish Stops capturing traces and dumps traces to a specified path (stdout by default).\n"
|
|
" --overwrite Sets the action to take when the buffer is full. If this option is used,\n"
|
|
" the latest traces are discarded; if this option is not used (default setting),\n"
|
|
" the earliest traces are discarded.\n"
|
|
" -o filename Specifies the name of the target file (stdout by default).\n"
|
|
" --output filename\n"
|
|
" Like \"-o filename\".\n"
|
|
" -z Compresses a captured trace.\n"
|
|
);
|
|
}
|
|
|
|
template <typename T>
|
|
inline bool StrToNum(const std::string& sString, T &tX)
|
|
{
|
|
std::istringstream iStream(sString);
|
|
return (iStream >> tX) ? true : false;
|
|
}
|
|
|
|
static void ParseLongOpt(const string& cmd, int optionIndex, bool& isTrue)
|
|
{
|
|
if (!strcmp(g_longOptions[optionIndex].name, "buffer_size")) {
|
|
if (!StrToNum(optarg, g_bufferSizeKB)) {
|
|
fprintf(stderr, "Error: buffer size is illegal input. eg: \"--buffer_size 1024\"\n");
|
|
isTrue = false;
|
|
} else if (g_bufferSizeKB < MIN_BUFFER_SIZE || g_bufferSizeKB > MAX_BUFFER_SIZE) {
|
|
fprintf(stderr, "Error: buffer size must be from 256 KB to 300 MB. eg: \"--buffer_size 1024\"\n");
|
|
isTrue = false;
|
|
}
|
|
g_bufferSizeKB = g_bufferSizeKB / PAGE_SIZE_KB * PAGE_SIZE_KB;
|
|
} else if (!strcmp(g_longOptions[optionIndex].name, "trace_clock")) {
|
|
regex re("[a-zA-Z]{4,6}");
|
|
if (regex_match(optarg, re)) {
|
|
g_clock = optarg;
|
|
} else {
|
|
fprintf(stderr, "Error: \"--trace_clock\" is illegal input. eg: \"--trace_clock boot\"\n");
|
|
isTrue = false;
|
|
}
|
|
} else if (!strcmp(g_longOptions[optionIndex].name, "help")) {
|
|
ShowHelp(cmd);
|
|
isTrue = false;
|
|
} else if (!strcmp(g_longOptions[optionIndex].name, "time")) {
|
|
if (!StrToNum(optarg, g_traceDuration)) {
|
|
fprintf(stderr, "Error: the time is illegal input. eg: \"--time 5\"\n");
|
|
isTrue = false;
|
|
} else if (g_traceDuration < 1) {
|
|
fprintf(stderr, "Error: \"-t %s\" to be greater than zero. eg: \"--time 5\"\n", optarg);
|
|
isTrue = false;
|
|
}
|
|
} else if (!strcmp(g_longOptions[optionIndex].name, "list_categories")) {
|
|
ShowListCategory();
|
|
isTrue = false;
|
|
} else if (!strcmp(g_longOptions[optionIndex].name, "output")) {
|
|
struct stat buf;
|
|
size_t len = strnlen(optarg, MAX_OUTPUT_LEN);
|
|
if (len == MAX_OUTPUT_LEN || len < 1 || (stat(optarg, &buf) == 0 && (buf.st_mode & S_IFDIR))) {
|
|
fprintf(stderr, "Error: output file is illegal\n");
|
|
isTrue = false;
|
|
} else {
|
|
g_outputFile = optarg;
|
|
}
|
|
} else if (!strcmp(g_longOptions[optionIndex].name, "overwrite")) {
|
|
g_overwrite = false;
|
|
} else if (!strcmp(g_longOptions[optionIndex].name, "trace_begin")) {
|
|
g_traceStart = START_ASYNC;
|
|
g_traceStop = false;
|
|
g_traceDump = false;
|
|
} else if (!strcmp(g_longOptions[optionIndex].name, "trace_finish")) {
|
|
g_traceStart = START_NONE;
|
|
g_traceStop = true;
|
|
g_traceDump = true;
|
|
} else if (!strcmp(g_longOptions[optionIndex].name, "trace_dump")) {
|
|
g_traceStart = START_NONE;
|
|
g_traceStop = false;
|
|
g_traceDump = true;
|
|
}
|
|
}
|
|
|
|
static bool ParseOpt(int opt, char** argv, int optIndex)
|
|
{
|
|
bool isTrue = true;
|
|
switch (opt) {
|
|
case 'b': {
|
|
if (!StrToNum(optarg, g_bufferSizeKB)) {
|
|
fprintf(stderr, "Error: buffer size is illegal input. eg: \"--buffer_size 1024\"\n");
|
|
isTrue = false;
|
|
} else if (g_bufferSizeKB < MIN_BUFFER_SIZE || g_bufferSizeKB > MAX_BUFFER_SIZE) {
|
|
fprintf(stderr, "Error: buffer size must be from 256 KB to 300 MB. eg: \"--buffer_size 1024\"\n");
|
|
isTrue = false;
|
|
}
|
|
g_bufferSizeKB = g_bufferSizeKB / PAGE_SIZE_KB * PAGE_SIZE_KB;
|
|
break;
|
|
}
|
|
case 'h':
|
|
ShowHelp(argv[0]);
|
|
isTrue = false;
|
|
break;
|
|
case 'l':
|
|
ShowListCategory();
|
|
isTrue = false;
|
|
break;
|
|
case 't': {
|
|
if (!StrToNum(optarg, g_traceDuration)) {
|
|
fprintf(stderr, "Error: the time is illegal input. eg: \"--time 5\"\n");
|
|
isTrue = false;
|
|
} else if (g_traceDuration < 1) {
|
|
fprintf(stderr, "Error: \"-t %s\" to be greater than zero. eg: \"--time 5\"\n", optarg);
|
|
isTrue = false;
|
|
}
|
|
break;
|
|
}
|
|
case 'o': {
|
|
struct stat buf;
|
|
size_t len = strnlen(optarg, MAX_OUTPUT_LEN);
|
|
if (len == MAX_OUTPUT_LEN || len < 1 || (stat(optarg, &buf) == 0 && (buf.st_mode & S_IFDIR))) {
|
|
fprintf(stderr, "Error: output file is illegal\n");
|
|
isTrue = false;
|
|
} else {
|
|
g_outputFile = optarg;
|
|
}
|
|
break;
|
|
}
|
|
case 'z':
|
|
g_compress = true;
|
|
break;
|
|
case 0: // long options
|
|
ParseLongOpt(argv[0], optIndex, isTrue);
|
|
break;
|
|
default:
|
|
ShowHelp(argv[0]);
|
|
isTrue = false;
|
|
break;
|
|
}
|
|
return isTrue;
|
|
}
|
|
|
|
static void IsInvalidOpt(int argc, char** argv)
|
|
{
|
|
for (int i = optind; i < argc; i++) {
|
|
if (!IsTagSupported(argv[i])) {
|
|
fprintf(stderr, "Error: \"%s\" is not support category on this device\n", argv[i]);
|
|
exit(-1);
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool HandleOpt(int argc, char** argv)
|
|
{
|
|
bool isTrue = true;
|
|
int opt = 0;
|
|
int optionIndex = 0;
|
|
string shortOption = "b:c:hlo:t:z";
|
|
int argcSize = argc;
|
|
while (isTrue && argcSize-- > 0) {
|
|
opt = getopt_long(argc, argv, shortOption.c_str(), g_longOptions, &optionIndex);
|
|
if (opt < 0) {
|
|
IsInvalidOpt(argc, argv);
|
|
break;
|
|
}
|
|
isTrue = ParseOpt(opt, argv, optionIndex);
|
|
}
|
|
return isTrue;
|
|
}
|
|
|
|
static bool TruncateFile(const string& path)
|
|
{
|
|
int fd = creat((g_traceRootPath + path).c_str(), 0);
|
|
if (fd == -1) {
|
|
fprintf(stderr, "Error: clear %s, errno: %d\n", (g_traceRootPath + path).c_str(), errno);
|
|
return false;
|
|
}
|
|
close(fd);
|
|
fd = -1;
|
|
return true;
|
|
}
|
|
|
|
static bool ClearTrace()
|
|
{
|
|
return TruncateFile(TRACE_PATH);
|
|
}
|
|
|
|
static bool StartTrace()
|
|
{
|
|
if (!SetFtraceEnabled(TRACING_ON_PATH, true)) {
|
|
return false;
|
|
}
|
|
ClearTrace();
|
|
printf("capturing trace...\n");
|
|
fflush(stdout);
|
|
return true;
|
|
}
|
|
|
|
static void WaitForTraceDone(void)
|
|
{
|
|
struct timespec ts = {0, 0};
|
|
ts.tv_sec = g_traceDuration;
|
|
ts.tv_nsec = 0;
|
|
while ((nanosleep(&ts, &ts) == -1) && (errno == EINTR)) {}
|
|
}
|
|
|
|
static bool StopTrace()
|
|
{
|
|
return SetFtraceEnabled(TRACING_ON_PATH, false);
|
|
}
|
|
|
|
static void DumpCompressedTrace(int traceFd, int outFd)
|
|
{
|
|
z_stream zs { nullptr };
|
|
ssize_t bytesWritten;
|
|
ssize_t bytesRead;
|
|
if (memset_s(&zs, sizeof(zs), 0, sizeof(zs)) != 0) {
|
|
fprintf(stderr, "Error: zip stream buffer init failed\n");
|
|
return;
|
|
}
|
|
int ret = deflateInit(&zs, Z_DEFAULT_COMPRESSION);
|
|
if (ret != Z_OK) {
|
|
fprintf(stderr, "Error: initializing zlib: %d\n", ret);
|
|
return;
|
|
}
|
|
unsigned int have = 0;
|
|
std::unique_ptr<uint8_t[]> in = std::make_unique<uint8_t[]>(CHUNK_SIZE);
|
|
std::unique_ptr<uint8_t[]> out = std::make_unique<uint8_t[]>(CHUNK_SIZE);
|
|
int flush = Z_NO_FLUSH;
|
|
if (!in || !out) {
|
|
fprintf(stderr, "Error: couldn't allocate buffers\n");
|
|
return;
|
|
}
|
|
do {
|
|
bytesRead = TEMP_FAILURE_RETRY(read(traceFd, in.get(), CHUNK_SIZE));
|
|
if (bytesRead == 0) {
|
|
flush = Z_FINISH;
|
|
} else if (bytesRead == -1) {
|
|
fprintf(stderr, "Error: reading trace, errno: %d\n", errno);
|
|
break;
|
|
}
|
|
zs.next_in = reinterpret_cast<Bytef*>(in.get());
|
|
zs.avail_in = bytesRead;
|
|
do {
|
|
zs.next_out = reinterpret_cast<Bytef*>(out.get());
|
|
zs.avail_out = CHUNK_SIZE;
|
|
ret = deflate(&zs, flush);
|
|
if (ret != Z_OK) {
|
|
fprintf(stderr, "Error: deflate zlib: %d\n", ret);
|
|
break;
|
|
}
|
|
have = CHUNK_SIZE - zs.avail_out;
|
|
bytesWritten = TEMP_FAILURE_RETRY(write(outFd, out.get(), have));
|
|
if (bytesWritten < 0 || (static_cast<size_t>(bytesWritten) < static_cast<size_t>(have)) ||
|
|
bytesWritten == -1) {
|
|
fprintf(stderr, "Error: writing deflated trace, errno: %d\n", errno);
|
|
break;
|
|
}
|
|
} while (zs.avail_out == 0);
|
|
} while (flush != Z_FINISH);
|
|
deflateEnd(&zs);
|
|
}
|
|
|
|
static void DumpTrace(int outFd, const string& path)
|
|
{
|
|
string resolvedPath = CanonicalizeSpecPath((g_traceRootPath + path).c_str());
|
|
int traceFd = open(resolvedPath.c_str(), O_RDWR);
|
|
if (traceFd == -1) {
|
|
fprintf(stderr, "error opening %s, errno: %d\n", path.c_str(), errno);
|
|
return;
|
|
}
|
|
ssize_t bytesWritten;
|
|
ssize_t bytesRead;
|
|
if (g_compress) {
|
|
DumpCompressedTrace(traceFd, outFd);
|
|
} else {
|
|
char buffer[BLOCK_SIZE];
|
|
do {
|
|
bytesRead = TEMP_FAILURE_RETRY(read(traceFd, buffer, BLOCK_SIZE));
|
|
if ((bytesRead == 0) || (bytesRead == -1)) {
|
|
break;
|
|
}
|
|
bytesWritten = TEMP_FAILURE_RETRY(write(outFd, buffer, bytesRead));
|
|
} while (bytesWritten > 0);
|
|
}
|
|
close(traceFd);
|
|
}
|
|
|
|
static bool MarkOthersClockSync()
|
|
{
|
|
constexpr unsigned int bufferSize = 128; // buffer size
|
|
char buffer[bufferSize] = { 0 };
|
|
string resolvedPath = CanonicalizeSpecPath((g_traceRootPath + TRACE_MARKER_PATH).c_str());
|
|
int fd = open(resolvedPath.c_str(), O_WRONLY);
|
|
if (fd == -1) {
|
|
fprintf(stderr, "Error: opening %s, errno: %d\n", TRACE_MARKER_PATH.c_str(), errno);
|
|
return false;
|
|
}
|
|
|
|
struct timespec mts = {0, 0};
|
|
struct timespec rts = {0, 0};
|
|
if (clock_gettime(CLOCK_REALTIME, &rts) == -1) {
|
|
fprintf(stderr, "Error: get realtime, errno: %d\n", errno);
|
|
close(fd);
|
|
return false;
|
|
} else if (clock_gettime(CLOCK_MONOTONIC, &mts) == -1) {
|
|
fprintf(stderr, "Error: get parent_ts, errno: %d\n", errno);
|
|
close(fd);
|
|
return false;
|
|
}
|
|
constexpr unsigned int nanoSeconds = 1000000000; // seconds converted to nanoseconds
|
|
constexpr unsigned int nanoToMill = 1000000; // millisecond converted to nanoseconds
|
|
constexpr float nanoToSecond = 1000000000.0f; // consistent with the ftrace timestamp format
|
|
int len = snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1,
|
|
"trace_event_clock_sync: realtime_ts=%" PRId64 "\n",
|
|
static_cast<int64_t>((rts.tv_sec * nanoSeconds + rts.tv_nsec) / nanoToMill));
|
|
if (len < 0) {
|
|
fprintf(stderr, "Error: entering data into buffer, errno: %d\n", errno);
|
|
close(fd);
|
|
return false;
|
|
}
|
|
if (write(fd, buffer, len) < 0) {
|
|
fprintf(stderr, "Warning: writing clock sync marker, errno: %d\n", errno);
|
|
fprintf(stderr, "the buffer is not enough, please increase the buffer\n");
|
|
}
|
|
len = snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, "trace_event_clock_sync: parent_ts=%f\n",
|
|
static_cast<float>(((static_cast<float>(mts.tv_sec)) * nanoSeconds + mts.tv_nsec) / nanoToSecond));
|
|
if (len < 0) {
|
|
fprintf(stderr, "Error: entering data into buffer, errno: %d\n", errno);
|
|
close(fd);
|
|
return false;
|
|
}
|
|
if (write(fd, buffer, len) < 0) {
|
|
fprintf(stderr, "Warning: writing clock sync marker, errno: %d\n", errno);
|
|
fprintf(stderr, "the buffer is not enough, please increase the buffer\n");
|
|
}
|
|
close(fd);
|
|
return true;
|
|
}
|
|
|
|
static void InitDiskSupportTags()
|
|
{
|
|
g_tagMap["disk"] = { "disk", "Disk I/O", 0, KERNEL, {
|
|
{ "events/f2fs/f2fs_sync_file_enter/enable" },
|
|
{ "events/f2fs/f2fs_sync_file_exit/enable" },
|
|
{ "events/f2fs/f2fs_write_begin/enable" },
|
|
{ "events/f2fs/f2fs_write_end/enable" },
|
|
{ "events/ext4/ext4_da_write_begin/enable" },
|
|
{ "events/ext4/ext4_da_write_end/enable" },
|
|
{ "events/ext4/ext4_sync_file_enter/enable" },
|
|
{ "events/ext4/ext4_sync_file_exit/enable" },
|
|
{ "events/block/block_rq_issue/enable" },
|
|
{ "events/block/block_rq_complete/enable" },
|
|
}};
|
|
g_tagMap["mmc"] = { "mmc", "eMMC commands", 0, KERNEL, {
|
|
{ "events/mmc/enable" },
|
|
}};
|
|
g_tagMap["ufs"] = { "ufs", "UFS commands", 0, KERNEL, {
|
|
{ "events/ufs/enable" },
|
|
}};
|
|
}
|
|
|
|
static void InitHardwareSupportTags()
|
|
{
|
|
g_tagMap["irq"] = { "irq", "IRQ Events", 0, KERNEL, {
|
|
{ "events/irq/enable" },
|
|
{ "events/ipi/enable" },
|
|
}};
|
|
g_tagMap["irqoff"] = { "irqoff", "IRQ-disabled code section tracing", 0, KERNEL, {
|
|
{ "events/preemptirq/irq_enable/enable" },
|
|
{ "events/preemptirq/irq_disable/enable" },
|
|
}};
|
|
InitDiskSupportTags();
|
|
g_tagMap["i2c"] = { "i2c", "I2C Events", 0, KERNEL, {
|
|
{ "events/i2c/enable" },
|
|
{ "events/i2c/i2c_read/enable" },
|
|
{ "events/i2c/i2c_write/enable" },
|
|
{ "events/i2c/i2c_result/enable" },
|
|
{ "events/i2c/i2c_reply/enable" },
|
|
{ "events/i2c/smbus_read/enable" },
|
|
{ "events/i2c/smbus_write/enable" },
|
|
{ "events/i2c/smbus_result/enable" },
|
|
{ "events/i2c/smbus_reply/enable" },
|
|
}};
|
|
g_tagMap["regulators"] = { "regulators", "Voltage and Current Regulators", 0, KERNEL, {
|
|
{ "events/regulator/enable" },
|
|
}};
|
|
g_tagMap["membus"] = { "membus", "Memory Bus Utilization", 0, KERNEL, {
|
|
{ "events/memory_bus/enable" },
|
|
}};
|
|
}
|
|
|
|
static void InitCpuSupportTags()
|
|
{
|
|
g_tagMap["freq"] = { "freq", "CPU Frequency", 0, KERNEL, {
|
|
{ "events/power/cpu_frequency/enable" },
|
|
{ "events/power/clock_set_rate/enable" },
|
|
{ "events/power/clock_disable/enable" },
|
|
{ "events/power/clock_enable/enable" },
|
|
{ "events/clk/clk_set_rate/enable" },
|
|
{ "events/clk/clk_disable/enable" },
|
|
{ "events/clk/clk_enable/enable" },
|
|
{ "events/power/cpu_frequency_limits/enable" },
|
|
}};
|
|
g_tagMap["idle"] = { "idle", "CPU Idle", 0, KERNEL, {
|
|
{ "events/power/cpu_idle/enable" },
|
|
}};
|
|
g_tagMap["load"] = { "load", "CPU Load", 0, KERNEL, {
|
|
{ "events/cpufreq_interactive/enable" },
|
|
}};
|
|
}
|
|
|
|
static void InitKernelSupportTags()
|
|
{
|
|
g_tagMap["sched"] = { "sched", "CPU Scheduling", 0, KERNEL, {
|
|
{ "events/sched/sched_switch/enable" },
|
|
{ "events/sched/sched_wakeup/enable" },
|
|
{ "events/sched/sched_wakeup_new/enable" },
|
|
{ "events/sched/sched_waking/enable" },
|
|
{ "events/sched/sched_blocked_reason/enable" },
|
|
{ "events/sched/sched_pi_setprio/enable" },
|
|
{ "events/sched/sched_process_exit/enable" },
|
|
{ "events/cgroup/enable" },
|
|
{ "events/oom/oom_score_adj_update/enable" },
|
|
{ "events/task/task_rename/enable" },
|
|
{ "events/task/task_newtask/enable" },
|
|
}};
|
|
g_tagMap["preemptoff"] = { "preemptoff", "Preempt-disabled code section tracing", 0, KERNEL, {
|
|
{ "events/preemptirq/preempt_enable/enable" },
|
|
{ "events/preemptirq/preempt_disable/enable" },
|
|
}};
|
|
|
|
g_tagMap["binder"] = { "binder", "Binder kernel Info", 0, KERNEL, {
|
|
{ "events/binder/binder_transaction/enable" },
|
|
{ "events/binder/binder_transaction_received/enable" },
|
|
{ "events/binder/binder_transaction_alloc_buf/enable" },
|
|
{ "events/binder/binder_set_priority/enable" },
|
|
{ "events/binder/binder_lock/enable" },
|
|
{ "events/binder/binder_locked/enable" },
|
|
{ "events/binder/binder_unlock/enable" },
|
|
}};
|
|
|
|
g_tagMap["sync"] = { "sync", "Synchronization", 0, KERNEL, {
|
|
// linux kernel > 4.9
|
|
{ "events/dma_fence/enable" },
|
|
}};
|
|
g_tagMap["workq"] = { "workq", "Kernel Workqueues", 0, KERNEL, {
|
|
{ "events/workqueue/enable" },
|
|
}};
|
|
g_tagMap["memreclaim"] = { "memreclaim", "Kernel Memory Reclaim", 0, KERNEL, {
|
|
{ "events/vmscan/mm_vmscan_direct_reclaim_begin/enable" },
|
|
{ "events/vmscan/mm_vmscan_direct_reclaim_end/enable" },
|
|
{ "events/vmscan/mm_vmscan_kswapd_wake/enable" },
|
|
{ "events/vmscan/mm_vmscan_kswapd_sleep/enable" },
|
|
{ "events/lowmemorykiller/enable" },
|
|
}};
|
|
g_tagMap["pagecache"] = { "pagecache", "Page cache", 0, KERNEL, {
|
|
{ "events/filemap/enable" },
|
|
}};
|
|
g_tagMap["memory"] = { "memory", "Memory", 0, KERNEL, {
|
|
{ "events/kmem/rss_stat/enable" },
|
|
{ "events/kmem/ion_heap_grow/enable" },
|
|
{ "events/kmem/ion_heap_shrink/enable" },
|
|
}};
|
|
InitCpuSupportTags();
|
|
InitHardwareSupportTags();
|
|
}
|
|
|
|
static void InitAllSupportTags()
|
|
{
|
|
// OHOS
|
|
g_tagMap["ohos"] = { "ohos", "OpenHarmony", HITRACE_TAG_OHOS, USER, {}};
|
|
g_tagMap["ability"] = { "ability", "Ability Manager", HITRACE_TAG_ABILITY_MANAGER, USER, {}};
|
|
g_tagMap["zcamera"] = { "zcamera", "OpenHarmony Camera Module", HITRACE_TAG_ZCAMERA, USER, {}};
|
|
g_tagMap["zmedia"] = { "zmedia", "OpenHarmony Media Module", HITRACE_TAG_ZMEDIA, USER, {}};
|
|
g_tagMap["zimage"] = { "zimage", "OpenHarmony Image Module", HITRACE_TAG_ZIMAGE, USER, {}};
|
|
g_tagMap["zaudio"] = { "zaudio", "OpenHarmony Audio Module", HITRACE_TAG_ZAUDIO, USER, {}};
|
|
g_tagMap["distributeddatamgr"] = { "distributeddatamgr", "Distributed Data Manager",
|
|
HITRACE_TAG_DISTRIBUTEDDATA, USER, {}};
|
|
g_tagMap["mdfs"] = { "mdfs", "Mobile Distributed File System", HITRACE_TAG_MDFS, USER, {}};
|
|
g_tagMap["graphic"] = { "graphic", "Graphic Module", HITRACE_TAG_GRAPHIC_AGP, USER, {}};
|
|
g_tagMap["ace"] = { "ace", "ACE development framework", HITRACE_TAG_ACE, USER, {}};
|
|
g_tagMap["notification"] = { "notification", "Notification Module", HITRACE_TAG_NOTIFICATION, USER, {}};
|
|
g_tagMap["misc"] = { "misc", "Misc Module", HITRACE_TAG_MISC, USER, {}};
|
|
g_tagMap["multimodalinput"] = { "multimodalinput", "Multimodal Input Module",
|
|
HITRACE_TAG_MULTIMODALINPUT, USER, {}};
|
|
g_tagMap["sensors"] = { "sensors", "Sensors Module", HITRACE_TAG_SENSORS, USER, {}};
|
|
g_tagMap["msdp"] = { "msdp", "Multimodal Sensor Data Platform", HITRACE_TAG_MSDP, USER, {}};
|
|
g_tagMap["dsoftbus"] = { "dsoftbus", "Distributed Softbus", HITRACE_TAG_DSOFTBUS, USER, {}};
|
|
g_tagMap["rpc"] = { "rpc", "RPC and IPC", HITRACE_TAG_RPC, USER, {}};
|
|
g_tagMap["ark"] = { "ark", "ARK Module", HITRACE_TAG_ARK, USER, {}};
|
|
g_tagMap["window"] = { "window", "Window Manager", HITRACE_TAG_WINDOW_MANAGER, USER, {}};
|
|
g_tagMap["accessibility"] = { "accessibility", "Accessibility Manager",
|
|
HITRACE_TAG_ACCESSIBILITY_MANAGER, USER, {}};
|
|
g_tagMap["account"] = { "account", "Account Manager", HITRACE_TAG_ACCOUNT_MANAGER, USER, {}};
|
|
g_tagMap["dhfwk"] = { "dhfwk", "Distributed Hardware FWK", HITRACE_TAG_DISTRIBUTED_HARDWARE_FWK, USER, {}};
|
|
g_tagMap["dscreen"] = { "dscreen", "Distributed Screen", HITRACE_TAG_DISTRIBUTED_SCREEN, USER, {}};
|
|
g_tagMap["dcamera"] = { "dcamera", "Distributed Camera", HITRACE_TAG_DISTRIBUTED_CAMERA, USER, {}};
|
|
g_tagMap["dinput"] = { "dinput", "Distributed Input", HITRACE_TAG_DISTRIBUTED_INPUT, USER, {}};
|
|
g_tagMap["devicemanager"] = { "devicemanager", "Device Manager", HITRACE_TAG_DEVICE_MANAGER, USER, {}};
|
|
g_tagMap["deviceprofile"] = { "deviceprofile", "Device Profile", HITRACE_TAG_DEVICE_PROFILE, USER, {}};
|
|
g_tagMap["dsched"] = { "dsched", "Distributed Schedule", HITRACE_TAG_DISTRIBUTED_SCHEDULE, USER, {}};
|
|
g_tagMap["samgr"] = { "samgr", "samgr", HITRACE_TAG_SAMGR, USER, {}};
|
|
g_tagMap["app"] = { "app", "APP Module", HITRACE_TAG_APP, USER, {}};
|
|
g_tagMap["zbinder"] = { "zbinder", "OpenHarmony binder communication", 0, KERNEL, {
|
|
{ "events/zbinder/enable" },
|
|
}};
|
|
g_tagMap["gresource"] = { "gresource", "Global Resource Manager", HITRACE_TAG_GLOBAL_RESMGR, USER, {}};
|
|
g_tagMap["power"] = { "power", "Power Manager", HITRACE_TAG_POWER, USER, {}};
|
|
|
|
// Kernel os
|
|
InitKernelSupportTags();
|
|
}
|
|
|
|
static void InterruptExit(int signo)
|
|
{
|
|
_exit(-1);
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
(void)signal(SIGKILL, InterruptExit);
|
|
(void)signal(SIGINT, InterruptExit);
|
|
|
|
if (!IsTraceMounted()) {
|
|
exit(-1);
|
|
}
|
|
|
|
InitAllSupportTags();
|
|
|
|
if (!HandleOpt(argc, argv)) {
|
|
exit(-1);
|
|
}
|
|
|
|
if (!SetKernelSpaceSettings()) {
|
|
ClearKernelSpaceSettings();
|
|
exit(-1);
|
|
}
|
|
|
|
if (!SetUserSpaceSettings()) {
|
|
ClearKernelSpaceSettings();
|
|
ClearUserSpaceSettings();
|
|
exit(-1);
|
|
}
|
|
|
|
bool isTrue = true;
|
|
if (g_traceStart != START_NONE) {
|
|
SetViewStyle();
|
|
isTrue = isTrue && StartTrace();
|
|
if (g_traceStart == START_ASYNC) {
|
|
return isTrue ? 0 : -1;
|
|
}
|
|
WaitForTraceDone();
|
|
}
|
|
|
|
isTrue = isTrue && MarkOthersClockSync();
|
|
|
|
if (g_traceStop) {
|
|
isTrue = isTrue && StopTrace();
|
|
}
|
|
|
|
if (isTrue && g_traceDump) {
|
|
int outFd = STDOUT_FILENO;
|
|
if (g_outputFile.size() > 0) {
|
|
printf("write trace to %s\n", g_outputFile.c_str());
|
|
string resolvedPath = CanonicalizeSpecPath(g_outputFile.c_str());
|
|
outFd = open(resolvedPath.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
|
}
|
|
if (outFd == -1) {
|
|
fprintf(stderr, "Failed to open file '%s', err=%d", g_outputFile.c_str(), errno);
|
|
isTrue = false;
|
|
} else {
|
|
dprintf(outFd, "TRACE:\n");
|
|
DumpTrace(outFd, TRACE_PATH);
|
|
if (outFd != STDOUT_FILENO) {
|
|
close(outFd);
|
|
outFd = -1;
|
|
}
|
|
}
|
|
ClearTrace();
|
|
}
|
|
|
|
ClearUserSpaceSettings();
|
|
ClearKernelSpaceSettings();
|
|
|
|
return isTrue ? 0 : -1;
|
|
}
|