1d9f0492fSopenharmony_ci/* 2d9f0492fSopenharmony_ci * Copyright (c) 2022 Huawei Device Co., Ltd. 3d9f0492fSopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License"); 4d9f0492fSopenharmony_ci * you may not use this file except in compliance with the License. 5d9f0492fSopenharmony_ci * You may obtain a copy of the License at 6d9f0492fSopenharmony_ci * 7d9f0492fSopenharmony_ci * http://www.apache.org/licenses/LICENSE-2.0 8d9f0492fSopenharmony_ci * 9d9f0492fSopenharmony_ci * Unless required by applicable law or agreed to in writing, software 10d9f0492fSopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS, 11d9f0492fSopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12d9f0492fSopenharmony_ci * See the License for the specific language governing permissions and 13d9f0492fSopenharmony_ci * limitations under the License. 14d9f0492fSopenharmony_ci */ 15d9f0492fSopenharmony_ci 16d9f0492fSopenharmony_ci#include "bootchart.h" 17d9f0492fSopenharmony_ci 18d9f0492fSopenharmony_ci#include <dirent.h> 19d9f0492fSopenharmony_ci#include <stdint.h> 20d9f0492fSopenharmony_ci#include <sys/utsname.h> 21d9f0492fSopenharmony_ci#include <sys/time.h> 22d9f0492fSopenharmony_ci#include <time.h> 23d9f0492fSopenharmony_ci#include <unistd.h> 24d9f0492fSopenharmony_ci 25d9f0492fSopenharmony_ci#include "init_module_engine.h" 26d9f0492fSopenharmony_ci#include "init_param.h" 27d9f0492fSopenharmony_ci#include "init_utils.h" 28d9f0492fSopenharmony_ci#include "plugin_adapter.h" 29d9f0492fSopenharmony_ci#include "securec.h" 30d9f0492fSopenharmony_ci 31d9f0492fSopenharmony_ci#define NANO_PRE_JIFFY 10000000 32d9f0492fSopenharmony_ci#define BOOTCHART_OUTPUT_PATH "/data/service/el0/startup/init/" 33d9f0492fSopenharmony_ci 34d9f0492fSopenharmony_cistatic BootchartCtrl *g_bootchartCtrl = NULL; 35d9f0492fSopenharmony_ci 36d9f0492fSopenharmony_ciBOOTCHART_STATIC long long GetJiffies(void) 37d9f0492fSopenharmony_ci{ 38d9f0492fSopenharmony_ci struct timespec time1 = {0}; 39d9f0492fSopenharmony_ci clock_gettime(CLOCK_MONOTONIC, &time1); 40d9f0492fSopenharmony_ci long long jiffies1 = (long long)time1.tv_nsec / NANO_PRE_JIFFY; 41d9f0492fSopenharmony_ci long long jiffies2 = (long long)time1.tv_sec * (1000000000 / NANO_PRE_JIFFY); // 1000000000 to nsec 42d9f0492fSopenharmony_ci return jiffies1 + jiffies2; 43d9f0492fSopenharmony_ci} 44d9f0492fSopenharmony_ci 45d9f0492fSopenharmony_cichar *ReadFileToBuffer(const char *fileName, char *buffer, uint32_t bufferSize) 46d9f0492fSopenharmony_ci{ 47d9f0492fSopenharmony_ci PLUGIN_CHECK(buffer != NULL && fileName != NULL, return NULL, "Invalid param"); 48d9f0492fSopenharmony_ci int fd = -1; 49d9f0492fSopenharmony_ci ssize_t readLen = 0; 50d9f0492fSopenharmony_ci do { 51d9f0492fSopenharmony_ci buffer[0] = '\0'; 52d9f0492fSopenharmony_ci errno = 0; 53d9f0492fSopenharmony_ci fd = open(fileName, O_RDONLY); 54d9f0492fSopenharmony_ci if (fd > 0) { 55d9f0492fSopenharmony_ci readLen = read(fd, buffer, bufferSize - 1); 56d9f0492fSopenharmony_ci } 57d9f0492fSopenharmony_ci PLUGIN_CHECK(readLen >= 0, break, "Failed to read data for %s %d readLen %d", fileName, errno, readLen); 58d9f0492fSopenharmony_ci buffer[readLen] = '\0'; 59d9f0492fSopenharmony_ci } while (0); 60d9f0492fSopenharmony_ci if (fd != -1) { 61d9f0492fSopenharmony_ci close(fd); 62d9f0492fSopenharmony_ci } 63d9f0492fSopenharmony_ci return (readLen > 0) ? buffer : NULL; 64d9f0492fSopenharmony_ci} 65d9f0492fSopenharmony_ci 66d9f0492fSopenharmony_ciBOOTCHART_STATIC void BootchartLogHeader(void) 67d9f0492fSopenharmony_ci{ 68d9f0492fSopenharmony_ci char date[32]; // 32 data size 69d9f0492fSopenharmony_ci time_t tm = time(NULL); 70d9f0492fSopenharmony_ci PLUGIN_CHECK(tm >= 0, return, "Failed to get time"); 71d9f0492fSopenharmony_ci struct tm *now = localtime(&tm); 72d9f0492fSopenharmony_ci PLUGIN_CHECK(now != NULL, return, "Failed to get local time"); 73d9f0492fSopenharmony_ci size_t size = strftime(date, sizeof(date), "%F %T", now); 74d9f0492fSopenharmony_ci PLUGIN_CHECK(size > 0, return, "Failed to strftime"); 75d9f0492fSopenharmony_ci struct utsname uts; 76d9f0492fSopenharmony_ci if (uname(&uts) == -1) { 77d9f0492fSopenharmony_ci return; 78d9f0492fSopenharmony_ci } 79d9f0492fSopenharmony_ci 80d9f0492fSopenharmony_ci char release[PARAM_VALUE_LEN_MAX] = {}; 81d9f0492fSopenharmony_ci uint32_t len = sizeof(release); 82d9f0492fSopenharmony_ci (void)SystemReadParam("const.ohos.releasetype", release, &len); 83d9f0492fSopenharmony_ci char *cmdLine = ReadFileToBuffer("/proc/cmdline", g_bootchartCtrl->buffer, g_bootchartCtrl->bufferSize); 84d9f0492fSopenharmony_ci PLUGIN_CHECK(cmdLine != NULL, return, "Failed to open file "BOOTCHART_OUTPUT_PATH"header"); 85d9f0492fSopenharmony_ci 86d9f0492fSopenharmony_ci FILE *file = fopen(BOOTCHART_OUTPUT_PATH"header", "we"); 87d9f0492fSopenharmony_ci PLUGIN_CHECK(file != NULL, return, "Failed to open file "BOOTCHART_OUTPUT_PATH"header"); 88d9f0492fSopenharmony_ci 89d9f0492fSopenharmony_ci (void)fprintf(file, "version = openharmony init\n"); 90d9f0492fSopenharmony_ci (void)fprintf(file, "title = Boot chart for openharmony (%s)\n", date); 91d9f0492fSopenharmony_ci (void)fprintf(file, "system.uname = %s %s %s %s\n", uts.sysname, uts.release, uts.version, uts.machine); 92d9f0492fSopenharmony_ci if (strlen(release) > 0) { 93d9f0492fSopenharmony_ci (void)fprintf(file, "system.release = %s\n", release); 94d9f0492fSopenharmony_ci } 95d9f0492fSopenharmony_ci (void)fprintf(file, "system.cpu = %s\n", uts.machine); 96d9f0492fSopenharmony_ci (void)fprintf(file, "system.kernel.options = %s\n", cmdLine); 97d9f0492fSopenharmony_ci (void)fclose(file); 98d9f0492fSopenharmony_ci} 99d9f0492fSopenharmony_ci 100d9f0492fSopenharmony_ciBOOTCHART_STATIC void BootchartLogFile(FILE *log, const char *procfile) 101d9f0492fSopenharmony_ci{ 102d9f0492fSopenharmony_ci (void)fprintf(log, "%lld\n", GetJiffies()); 103d9f0492fSopenharmony_ci char *data = ReadFileToBuffer(procfile, g_bootchartCtrl->buffer, g_bootchartCtrl->bufferSize); 104d9f0492fSopenharmony_ci if (data != NULL) { 105d9f0492fSopenharmony_ci (void)fprintf(log, "%s\n", data); 106d9f0492fSopenharmony_ci } 107d9f0492fSopenharmony_ci} 108d9f0492fSopenharmony_ci 109d9f0492fSopenharmony_ciBOOTCHART_STATIC void BootchartLogProcessStat(FILE *log, pid_t pid) 110d9f0492fSopenharmony_ci{ 111d9f0492fSopenharmony_ci static char path[255] = { }; // 255 path length 112d9f0492fSopenharmony_ci static char nameBuffer[255] = { }; // 255 path length 113d9f0492fSopenharmony_ci int ret = sprintf_s(path, sizeof(path) - 1, "/proc/%d/cmdline", pid); 114d9f0492fSopenharmony_ci PLUGIN_CHECK(ret > 0, return, "Failed to format path %d", pid); 115d9f0492fSopenharmony_ci path[ret] = '\0'; 116d9f0492fSopenharmony_ci 117d9f0492fSopenharmony_ci char *name = ReadFileToBuffer(path, nameBuffer, sizeof(nameBuffer)); 118d9f0492fSopenharmony_ci // Read process stat line 119d9f0492fSopenharmony_ci ret = sprintf_s(path, sizeof(path) - 1, "/proc/%d/stat", pid); 120d9f0492fSopenharmony_ci PLUGIN_CHECK(ret > 0, return, "Failed to format path %d", pid); 121d9f0492fSopenharmony_ci path[ret] = '\0'; 122d9f0492fSopenharmony_ci 123d9f0492fSopenharmony_ci char *stat = ReadFileToBuffer(path, g_bootchartCtrl->buffer, g_bootchartCtrl->bufferSize); 124d9f0492fSopenharmony_ci if (stat == NULL) { 125d9f0492fSopenharmony_ci return; 126d9f0492fSopenharmony_ci } 127d9f0492fSopenharmony_ci if (name != NULL && strlen(name) > 0) { 128d9f0492fSopenharmony_ci char *end = NULL; 129d9f0492fSopenharmony_ci char *start = strstr(stat, "("); 130d9f0492fSopenharmony_ci if (start != NULL) { 131d9f0492fSopenharmony_ci end = strstr(start, ")"); 132d9f0492fSopenharmony_ci } 133d9f0492fSopenharmony_ci if (end != NULL) { 134d9f0492fSopenharmony_ci stat[start - stat + 1] = '\0'; 135d9f0492fSopenharmony_ci (void)fputs(stat, log); 136d9f0492fSopenharmony_ci (void)fputs(name, log); 137d9f0492fSopenharmony_ci (void)fputs(end, log); 138d9f0492fSopenharmony_ci } else { 139d9f0492fSopenharmony_ci (void)fputs(stat, log); 140d9f0492fSopenharmony_ci } 141d9f0492fSopenharmony_ci } else { 142d9f0492fSopenharmony_ci (void)fputs(stat, log); 143d9f0492fSopenharmony_ci } 144d9f0492fSopenharmony_ci} 145d9f0492fSopenharmony_ci 146d9f0492fSopenharmony_ciBOOTCHART_STATIC void bootchartLogProcess(FILE *log) 147d9f0492fSopenharmony_ci{ 148d9f0492fSopenharmony_ci (void)fprintf(log, "%lld\n", GetJiffies()); 149d9f0492fSopenharmony_ci DIR *pDir = opendir("/proc"); 150d9f0492fSopenharmony_ci PLUGIN_CHECK(pDir != NULL, return, "Read dir /proc failed.%d", errno); 151d9f0492fSopenharmony_ci struct dirent *entry; 152d9f0492fSopenharmony_ci while ((entry = readdir(pDir)) != NULL) { 153d9f0492fSopenharmony_ci pid_t pid = (pid_t)atoi(entry->d_name); // Only process processor 154d9f0492fSopenharmony_ci if (pid == 0) { 155d9f0492fSopenharmony_ci continue; 156d9f0492fSopenharmony_ci } 157d9f0492fSopenharmony_ci BootchartLogProcessStat(log, pid); 158d9f0492fSopenharmony_ci } 159d9f0492fSopenharmony_ci closedir(pDir); 160d9f0492fSopenharmony_ci (void)fputc('\n', log); 161d9f0492fSopenharmony_ci} 162d9f0492fSopenharmony_ci 163d9f0492fSopenharmony_ciBOOTCHART_STATIC void *BootchartThreadMain(void *data) 164d9f0492fSopenharmony_ci{ 165d9f0492fSopenharmony_ci PLUGIN_LOGI("bootcharting start"); 166d9f0492fSopenharmony_ci FILE *statFile = fopen(BOOTCHART_OUTPUT_PATH"proc_stat.log", "w"); 167d9f0492fSopenharmony_ci FILE *procFile = fopen(BOOTCHART_OUTPUT_PATH"proc_ps.log", "w"); 168d9f0492fSopenharmony_ci FILE *diskFile = fopen(BOOTCHART_OUTPUT_PATH"proc_diskstats.log", "w"); 169d9f0492fSopenharmony_ci do { 170d9f0492fSopenharmony_ci if (statFile == NULL || procFile == NULL || diskFile == NULL) { 171d9f0492fSopenharmony_ci PLUGIN_LOGE("Failed to open file"); 172d9f0492fSopenharmony_ci break; 173d9f0492fSopenharmony_ci } 174d9f0492fSopenharmony_ci BootchartLogHeader(); 175d9f0492fSopenharmony_ci while (1) { 176d9f0492fSopenharmony_ci pthread_mutex_lock(&(g_bootchartCtrl->mutex)); 177d9f0492fSopenharmony_ci struct timespec abstime = {0}; 178d9f0492fSopenharmony_ci struct timeval now = {0}; 179d9f0492fSopenharmony_ci const long timeout = 200; // wait time 200ms 180d9f0492fSopenharmony_ci gettimeofday(&now, NULL); 181d9f0492fSopenharmony_ci long nsec = now.tv_usec * 1000 + (timeout % 1000) * 1000000; // 1000 unit 1000000 unit nsec 182d9f0492fSopenharmony_ci abstime.tv_sec = now.tv_sec + nsec / 1000000000 + timeout / 1000; // 1000 unit 1000000000 unit nsec 183d9f0492fSopenharmony_ci abstime.tv_nsec = nsec % 1000000000; // 1000000000 unit nsec 184d9f0492fSopenharmony_ci pthread_cond_timedwait(&(g_bootchartCtrl->cond), &(g_bootchartCtrl->mutex), &abstime); 185d9f0492fSopenharmony_ci if (g_bootchartCtrl->stop) { 186d9f0492fSopenharmony_ci pthread_mutex_unlock(&(g_bootchartCtrl->mutex)); 187d9f0492fSopenharmony_ci break; 188d9f0492fSopenharmony_ci } 189d9f0492fSopenharmony_ci pthread_mutex_unlock(&(g_bootchartCtrl->mutex)); 190d9f0492fSopenharmony_ci PLUGIN_LOGV("bootcharting running"); 191d9f0492fSopenharmony_ci BootchartLogFile(statFile, "/proc/stat"); 192d9f0492fSopenharmony_ci BootchartLogFile(diskFile, "/proc/diskstats"); 193d9f0492fSopenharmony_ci bootchartLogProcess(procFile); 194d9f0492fSopenharmony_ci } 195d9f0492fSopenharmony_ci } while (0); 196d9f0492fSopenharmony_ci 197d9f0492fSopenharmony_ci if (statFile != NULL) { 198d9f0492fSopenharmony_ci (void)fflush(statFile); 199d9f0492fSopenharmony_ci (void)fclose(statFile); 200d9f0492fSopenharmony_ci } 201d9f0492fSopenharmony_ci if (procFile != NULL) { 202d9f0492fSopenharmony_ci (void)fflush(procFile); 203d9f0492fSopenharmony_ci (void)fclose(procFile); 204d9f0492fSopenharmony_ci } 205d9f0492fSopenharmony_ci if (diskFile != NULL) { 206d9f0492fSopenharmony_ci (void)fflush(diskFile); 207d9f0492fSopenharmony_ci (void)fclose(diskFile); 208d9f0492fSopenharmony_ci } 209d9f0492fSopenharmony_ci PLUGIN_LOGI("bootcharting stop"); 210d9f0492fSopenharmony_ci return NULL; 211d9f0492fSopenharmony_ci} 212d9f0492fSopenharmony_ci 213d9f0492fSopenharmony_ciBOOTCHART_STATIC void BootchartDestory(void) 214d9f0492fSopenharmony_ci{ 215d9f0492fSopenharmony_ci pthread_mutex_destroy(&(g_bootchartCtrl->mutex)); 216d9f0492fSopenharmony_ci pthread_cond_destroy(&(g_bootchartCtrl->cond)); 217d9f0492fSopenharmony_ci free(g_bootchartCtrl); 218d9f0492fSopenharmony_ci g_bootchartCtrl = NULL; 219d9f0492fSopenharmony_ci} 220d9f0492fSopenharmony_ci 221d9f0492fSopenharmony_ciBOOTCHART_STATIC int DoBootchartStart(void) 222d9f0492fSopenharmony_ci{ 223d9f0492fSopenharmony_ci if (g_bootchartCtrl != NULL) { 224d9f0492fSopenharmony_ci PLUGIN_LOGI("bootcharting has been start"); 225d9f0492fSopenharmony_ci return 0; 226d9f0492fSopenharmony_ci } 227d9f0492fSopenharmony_ci g_bootchartCtrl = malloc(sizeof(BootchartCtrl)); 228d9f0492fSopenharmony_ci PLUGIN_CHECK(g_bootchartCtrl != NULL, return -1, "Failed to alloc mem for bootchart"); 229d9f0492fSopenharmony_ci g_bootchartCtrl->bufferSize = DEFAULT_BUFFER; 230d9f0492fSopenharmony_ci 231d9f0492fSopenharmony_ci int ret = pthread_mutex_init(&(g_bootchartCtrl->mutex), NULL); 232d9f0492fSopenharmony_ci PLUGIN_CHECK(ret == 0, BootchartDestory(); 233d9f0492fSopenharmony_ci return -1, "Failed to init mutex"); 234d9f0492fSopenharmony_ci ret = pthread_cond_init(&(g_bootchartCtrl->cond), NULL); 235d9f0492fSopenharmony_ci PLUGIN_CHECK(ret == 0, BootchartDestory(); 236d9f0492fSopenharmony_ci return -1, "Failed to init cond"); 237d9f0492fSopenharmony_ci 238d9f0492fSopenharmony_ci g_bootchartCtrl->stop = 0; 239d9f0492fSopenharmony_ci ret = pthread_create(&(g_bootchartCtrl->threadId), NULL, BootchartThreadMain, (void *)g_bootchartCtrl); 240d9f0492fSopenharmony_ci PLUGIN_CHECK(ret == 0, BootchartDestory(); 241d9f0492fSopenharmony_ci return -1, "Failed to init cond"); 242d9f0492fSopenharmony_ci 243d9f0492fSopenharmony_ci pthread_mutex_lock(&(g_bootchartCtrl->mutex)); 244d9f0492fSopenharmony_ci pthread_cond_signal(&(g_bootchartCtrl->cond)); 245d9f0492fSopenharmony_ci pthread_mutex_unlock(&(g_bootchartCtrl->mutex)); 246d9f0492fSopenharmony_ci g_bootchartCtrl->start = 1; 247d9f0492fSopenharmony_ci return 0; 248d9f0492fSopenharmony_ci} 249d9f0492fSopenharmony_ci 250d9f0492fSopenharmony_ciBOOTCHART_STATIC int DoBootchartStop(void) 251d9f0492fSopenharmony_ci{ 252d9f0492fSopenharmony_ci if (g_bootchartCtrl == NULL || !g_bootchartCtrl->start) { 253d9f0492fSopenharmony_ci PLUGIN_LOGI("bootcharting not start"); 254d9f0492fSopenharmony_ci return 0; 255d9f0492fSopenharmony_ci } 256d9f0492fSopenharmony_ci pthread_mutex_lock(&(g_bootchartCtrl->mutex)); 257d9f0492fSopenharmony_ci g_bootchartCtrl->stop = 1; 258d9f0492fSopenharmony_ci pthread_cond_signal(&(g_bootchartCtrl->cond)); 259d9f0492fSopenharmony_ci pthread_mutex_unlock(&(g_bootchartCtrl->mutex)); 260d9f0492fSopenharmony_ci pthread_join(g_bootchartCtrl->threadId, NULL); 261d9f0492fSopenharmony_ci BootchartDestory(); 262d9f0492fSopenharmony_ci PLUGIN_LOGI("bootcharting stopped"); 263d9f0492fSopenharmony_ci return 0; 264d9f0492fSopenharmony_ci} 265d9f0492fSopenharmony_ci 266d9f0492fSopenharmony_ciBOOTCHART_STATIC int DoBootchartCmd(int id, const char *name, int argc, const char **argv) 267d9f0492fSopenharmony_ci{ 268d9f0492fSopenharmony_ci PLUGIN_LOGI("DoBootchartCmd argc %d %s", argc, name); 269d9f0492fSopenharmony_ci PLUGIN_CHECK(argc >= 1, return -1, "Invalid parameter"); 270d9f0492fSopenharmony_ci if (strcmp(argv[0], "start") == 0) { 271d9f0492fSopenharmony_ci return DoBootchartStart(); 272d9f0492fSopenharmony_ci } else if (strcmp(argv[0], "stop") == 0) { 273d9f0492fSopenharmony_ci return DoBootchartStop(); 274d9f0492fSopenharmony_ci } 275d9f0492fSopenharmony_ci return 0; 276d9f0492fSopenharmony_ci} 277d9f0492fSopenharmony_ci 278d9f0492fSopenharmony_cistatic int32_t g_executorId = -1; 279d9f0492fSopenharmony_ciBOOTCHART_STATIC int BootchartInit(void) 280d9f0492fSopenharmony_ci{ 281d9f0492fSopenharmony_ci if (g_executorId == -1) { 282d9f0492fSopenharmony_ci g_executorId = AddCmdExecutor("bootchart", DoBootchartCmd); 283d9f0492fSopenharmony_ci PLUGIN_LOGI("BootchartInit executorId %d", g_executorId); 284d9f0492fSopenharmony_ci } 285d9f0492fSopenharmony_ci return 0; 286d9f0492fSopenharmony_ci} 287d9f0492fSopenharmony_ci 288d9f0492fSopenharmony_ciBOOTCHART_STATIC void BootchartExit(void) 289d9f0492fSopenharmony_ci{ 290d9f0492fSopenharmony_ci PLUGIN_LOGI("BootchartExit executorId %d", g_executorId); 291d9f0492fSopenharmony_ci if (g_executorId != -1) { 292d9f0492fSopenharmony_ci RemoveCmdExecutor("bootchart", g_executorId); 293d9f0492fSopenharmony_ci } 294d9f0492fSopenharmony_ci} 295d9f0492fSopenharmony_ci 296d9f0492fSopenharmony_ciMODULE_CONSTRUCTOR(void) 297d9f0492fSopenharmony_ci{ 298d9f0492fSopenharmony_ci PLUGIN_LOGI("DoBootchartStart now ..."); 299d9f0492fSopenharmony_ci BootchartInit(); 300d9f0492fSopenharmony_ci} 301d9f0492fSopenharmony_ci 302d9f0492fSopenharmony_ciMODULE_DESTRUCTOR(void) 303d9f0492fSopenharmony_ci{ 304d9f0492fSopenharmony_ci PLUGIN_LOGI("DoBootchartStop now ..."); 305d9f0492fSopenharmony_ci DoBootchartStop(); 306d9f0492fSopenharmony_ci BootchartExit(); 307d9f0492fSopenharmony_ci} 308