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
34 static BootchartCtrl *g_bootchartCtrl = NULL;
35
GetJiffies(void)36 BOOTCHART_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
ReadFileToBuffer(const char * fileName,char * buffer,uint32_t bufferSize)45 char *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
BootchartLogHeader(void)66 BOOTCHART_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
BootchartLogFile(FILE * log,const char * procfile)100 BOOTCHART_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
BootchartLogProcessStat(FILE * log,pid_t pid)109 BOOTCHART_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
bootchartLogProcess(FILE * log)146 BOOTCHART_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
BootchartThreadMain(void * data)163 BOOTCHART_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 = {};
178 struct timeval now = {};
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
BootchartDestory(void)213 BOOTCHART_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
DoBootchartStart(void)221 BOOTCHART_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
DoBootchartStop(void)250 BOOTCHART_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
DoBootchartCmd(int id,const char * name,int argc,const char ** argv)266 BOOTCHART_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
278 static int32_t g_executorId = -1;
BootchartInit(void)279 BOOTCHART_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
BootchartExit(void)288 BOOTCHART_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
MODULE_CONSTRUCTOR(void)296 MODULE_CONSTRUCTOR(void)
297 {
298 PLUGIN_LOGI("DoBootchartStart now ...");
299 BootchartInit();
300 }
301
MODULE_DESTRUCTOR(void)302 MODULE_DESTRUCTOR(void)
303 {
304 PLUGIN_LOGI("DoBootchartStop now ...");
305 DoBootchartStop();
306 BootchartExit();
307 }
308