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