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