| /**************************************************************************** |
| * apps/system/resmonitor/filldisk.c |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. The |
| * ASF licenses this file to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance with the |
| * License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations |
| * under the License. |
| * |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Included Files |
| ****************************************************************************/ |
| |
| #include <dirent.h> |
| #include <errno.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/select.h> |
| #include <sys/statfs.h> |
| #include <syslog.h> |
| #include <time.h> |
| #include <unistd.h> |
| |
| /**************************************************************************** |
| * Pre-processor Definitions |
| ****************************************************************************/ |
| |
| #define PATHSIZE 70 |
| #define FILESIZE 100 |
| #define FILENAME "testpayload" |
| #define INTERVAL 0 |
| |
| /**************************************************************************** |
| * Private Data |
| ****************************************************************************/ |
| |
| static time_t interval = INTERVAL; |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| static int getdiv(uintmax_t count) |
| { |
| int div = 1000; |
| if (count > 1000) |
| { |
| div = 1000; |
| } |
| else if (count > 100) |
| { |
| div = 100; |
| } |
| else if (count > 10) |
| { |
| div = 10; |
| } |
| else |
| { |
| div = 1; |
| } |
| |
| return div; |
| } |
| |
| static uintmax_t |
| get_fillsize(char *filepath, int mode, uintmax_t remain, uintmax_t fill) |
| { |
| struct statfs diskinfo; |
| int ret = statfs(filepath, &diskinfo); |
| if (ret != 0) |
| { |
| syslog(LOG_ERR, "statfs fail!\n"); |
| return -1; |
| } |
| |
| uintmax_t bsize = (uintmax_t)diskinfo.f_bsize; |
| uintmax_t bavail = (uintmax_t)diskinfo.f_bavail; |
| uintmax_t t_size = bsize * bavail; |
| uintmax_t fillsize = 0; |
| if (mode == 1) |
| { |
| fillsize = t_size - remain; |
| } |
| else if (mode == 2) |
| { |
| fillsize = (fill < t_size) ? fill : t_size; |
| } |
| |
| return fillsize; |
| } |
| |
| static clock_t fill_disk(char *file, uintmax_t bufsize, int mode, |
| uintmax_t remain, uintmax_t fill) |
| { |
| FILE *fp; |
| clock_t start, finish; |
| struct timeval sleeptime; |
| int ret; |
| if ((fp = fopen(file, "a+")) == NULL) |
| { |
| syslog(LOG_ERR, "Fail to open file!\n"); |
| return -1; |
| } |
| |
| uintmax_t fillsize = get_fillsize(file, mode, remain, fill); |
| if (fillsize <= 0) |
| { |
| syslog(LOG_WARNING, "out of space !!\n"); |
| fclose(fp); |
| return -1; |
| } |
| |
| char *buf = (char *)malloc((size_t)bufsize); |
| if (!buf) |
| { |
| syslog(LOG_ERR, "malloc fail, errno %d\n", errno); |
| fclose(fp); |
| return -1; |
| } |
| |
| memset(buf, 'a', (size_t)bufsize); |
| uintmax_t count = fillsize / bufsize; |
| int div = getdiv(count); |
| start = clock(); |
| while (fillsize > bufsize) |
| { |
| ret = fwrite(buf, 1, bufsize, fp); |
| if (ret > 0) |
| { |
| fillsize -= ret; |
| count--; |
| } |
| else |
| { |
| syslog(LOG_ERR, "write fail \n"); |
| fclose(fp); |
| free(buf); |
| return -1; |
| } |
| |
| sleeptime.tv_sec = interval / 1000000; |
| sleeptime.tv_usec = interval % 1000000; |
| select(0, NULL, NULL, NULL, &sleeptime); |
| if (count % div == 0) |
| { |
| syslog(LOG_INFO, "remain %ju B\n", fillsize); |
| fillsize = get_fillsize(file, mode, remain, fillsize); |
| if (fillsize < 0) |
| { |
| syslog(LOG_WARNING, "out of space !!\n"); |
| fclose(fp); |
| free(buf); |
| return -1; |
| } |
| |
| count = fillsize / bufsize; |
| div = getdiv(count); |
| } |
| } |
| |
| fwrite(buf, 1, fillsize, fp); |
| fflush(fp); |
| finish = clock(); |
| syslog(LOG_INFO, "write complete !!!\n"); |
| fclose(fp); |
| free(buf); |
| return finish - start; |
| } |
| |
| static void show_usages(void) |
| { |
| syslog(LOG_WARNING, |
| "Usage: CMD [-d <dir>] [-f <fill size>] [-r <remain size>] [-b " |
| "<write buf size>] [-n <file nums>] [-i <write interval (us)>]\n" |
| "\t\t-d: set dir to fill (e.g. /data)\n" |
| "\t\t-f: set fill file size, can end with KkMm, (disabled if -r is " |
| "set)\n" |
| "\t\t-n: works only if -f is set, create n files of size -f set, " |
| "default 1\n" |
| "\t\t-r: set remain size, can end with KkMm\n" |
| "\t\t-b: set write buf size, can end with kKMm, default is f_bsize\n" |
| "\t\t-i: set write intervals (us), default 0 us, set only if the " |
| "program killed by wdt\n"); |
| exit(1); |
| } |
| |
| static void double2str(double f, char *s, int len) |
| { |
| int ret = snprintf(s, len, "%.2lf", f); |
| if (ret < 0) |
| { |
| syslog(LOG_ERR, "double to str fail, ret %d\n", ret); |
| } |
| } |
| |
| static uintmax_t bytes(char *s) |
| { |
| uintmax_t n; |
| if (sscanf(s, "%ju", &n) < 1) |
| { |
| return 0; |
| } |
| |
| if ((s[strlen(s) - 1] == 'k') || (s[strlen(s) - 1] == 'K')) |
| { |
| n *= 1024; |
| } |
| |
| if ((s[strlen(s) - 1] == 'm') || (s[strlen(s) - 1] == 'M')) |
| { |
| n *= (1024 * 1024); |
| } |
| |
| return n; |
| } |
| |
| static void print_disk_info(struct statfs *diskinfo) |
| { |
| syslog(LOG_INFO, "\tfs block size : %zu\n", diskinfo->f_bsize); |
| syslog(LOG_INFO, |
| "\tfs block nums : %" PRIu64 "\n", |
| (uint64_t)diskinfo->f_blocks); |
| syslog(LOG_INFO, |
| "\tfs free blocks : %" PRIu64 "\n", |
| (uint64_t)diskinfo->f_bfree); |
| syslog(LOG_INFO, |
| "\tfs free blocks available : %" PRIu64 "\n", |
| (uint64_t)diskinfo->f_bavail); |
| syslog(LOG_INFO, |
| "\tfs total file nodes : %" PRIu64 "\n", |
| (uint64_t)diskinfo->f_files); |
| syslog(LOG_INFO, |
| "\tfs free file nodes : %" PRIu64 "\n", |
| (uint64_t)diskinfo->f_ffree); |
| } |
| |
| int main(int argc, FAR char *argv[]) |
| { |
| if (argc == 1) |
| { |
| show_usages(); |
| return -1; |
| } |
| |
| char filepath[PATHSIZE]; |
| char sduration_t[15]; |
| char sduration_w[15]; |
| char sspeed_t[15]; |
| char sspeed_w[15]; |
| uintmax_t fill = 0; |
| uintmax_t remain = 0; |
| uintmax_t bufsize = 0; |
| int nf = 1; |
| int mode = 0; |
| clock_t write_t = 0; |
| struct statfs diskinfo_old; |
| struct statfs diskinfo_new; |
| uintmax_t fillsize; |
| double duration_t; |
| double speed_t; |
| double duration_w; |
| double speed_w; |
| clock_t start; |
| clock_t finish; |
| clock_t tmp; |
| int ret; |
| int o; |
| |
| interval = INTERVAL; |
| memset(filepath, 0, PATHSIZE); |
| while ((o = getopt(argc, argv, "d:f:r:b:i:n:")) != EOF) |
| { |
| switch (o) |
| { |
| case 'd': |
| snprintf(filepath, PATHSIZE, "%s", optarg); |
| break; |
| case 'f': |
| fill = bytes(optarg); |
| if (mode != 1) |
| { |
| mode = 2; |
| } |
| |
| break; |
| case 'r': |
| remain = bytes(optarg); |
| mode = 1; |
| break; |
| case 'i': |
| interval = (time_t)bytes(optarg); |
| break; |
| case 'b': |
| bufsize = bytes(optarg); |
| break; |
| case 'n': |
| nf = atoi(optarg); |
| break; |
| default: |
| show_usages(); |
| break; |
| } |
| } |
| |
| if (strlen(filepath) == 0) |
| { |
| syslog(LOG_ERR, "please set dir \n"); |
| return -1; |
| } |
| |
| if (mode == 0) |
| { |
| syslog(LOG_ERR, "please set -f or -r \n"); |
| return -1; |
| } |
| else if (mode != 2) |
| { |
| nf = 1; |
| } |
| |
| ret = statfs(filepath, &diskinfo_old); |
| if (ret != 0) |
| { |
| syslog(LOG_ERR, "statfs fail!\n"); |
| return -1; |
| } |
| |
| if (bufsize == 0) |
| { |
| bufsize = diskinfo_old.f_bsize; |
| } |
| |
| fillsize = get_fillsize(filepath, mode, remain, fill) * nf; |
| syslog(LOG_INFO, "outputfilepath: %s\n", filepath); |
| start = clock(); |
| for (int i = 1; i <= nf; i++) |
| { |
| syslog(LOG_INFO, "create file %d, total %d\n", i, nf); |
| char file[FILESIZE]; |
| ret = snprintf(file, FILESIZE, "%s/%s%d", filepath, FILENAME, i); |
| if (ret < 0) |
| { |
| syslog(LOG_ERR, "snprintf err, ret %d\n", ret); |
| return -1; |
| } |
| |
| if ((tmp = fill_disk(file, bufsize, mode, remain, fill)) > 0) |
| { |
| write_t += tmp; |
| } |
| else |
| { |
| syslog(LOG_ERR, "fill disk error\n"); |
| return -1; |
| } |
| } |
| |
| finish = clock(); |
| ret = statfs(filepath, &diskinfo_new); |
| if (ret != 0) |
| { |
| syslog(LOG_ERR, "statfs fail!\n"); |
| return -1; |
| } |
| |
| syslog(LOG_INFO, "***diskinfo before filldisk***\n"); |
| print_disk_info(&diskinfo_old); |
| |
| syslog(LOG_INFO, "***diskinfo after filldisk***\n"); |
| print_disk_info(&diskinfo_new); |
| duration_t = (double)(finish - start) / CLOCKS_PER_SEC; |
| speed_t = fillsize / 1024.0 / duration_t; |
| duration_w = (double)(write_t) / CLOCKS_PER_SEC; |
| speed_w = fillsize / 1024.0 / duration_w; |
| double2str(speed_t, sspeed_t, 15); |
| double2str(speed_w, sspeed_w, 15); |
| double2str(duration_t, sduration_t, 15); |
| double2str(duration_w, sduration_w, 15); |
| syslog(LOG_INFO, "fill buf size: %ju\n", bufsize); |
| syslog(LOG_INFO, "total fill size: %ju\n", fillsize); |
| syslog(LOG_INFO, |
| "write speed (include open and close) %s KB/s, duration %s s\n", |
| sspeed_t, |
| sduration_t); |
| syslog(LOG_INFO, |
| "write speed (only write) %s KB/s, duration %s s\n", |
| sspeed_w, |
| sduration_w); |
| return 0; |
| } |