• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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