• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 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 #include <cstdlib>
16 #include <getopt.h>
17 #include <iostream>
18 #include <iomanip>
19 #include <ctime>
20 
21 #include <string_ex.h>
22 #include <securec.h>
23 #include <list>
24 
25 #include <hilog/log.h>
26 #include <hilog_common.h>
27 #include <log_utils.h>
28 #include <log_ioctl.h>
29 #include <log_print.h>
30 #include <properties.h>
31 
32 #include "log_display.h"
33 
34 namespace OHOS {
35 namespace HiviewDFX {
36 using namespace std;
QueryHelper()37 static void QueryHelper()
38 {
39     cout
40     << "Querying logs options:" << endl
41     << "  No option performs a blocking read and keeps printing." << endl
42     << "  -x --exit" << endl
43     << "    Performs a non-blocking read and exits when all logs in buffer are printed." << endl
44     << "  -a <n>, --head=<n>" << endl
45     << "    Show n lines logs on head of buffer." << endl
46     << "  -z <n>, --tail=<n>" << endl
47     << "    Show n lines logs on tail of buffer." << endl
48     << "  -t <type>, --type=<type>" << endl
49     << "    Show specific type/types logs with format: type1,type2,type3" << endl
50     << "    Don't show specific type/types logs with format: ^type1,type2,type3" << endl
51     << "    Type coule be: app/core/init/kmsg, kmsg can't combine with others." << endl
52     << "    Default types are: app,core,init." << endl
53     << "  -L <level>, --level=<level>" << endl
54     << "    Show specific level/levels logs with format: level1,level2,level3" << endl
55     << "    Don't show specific level/levels logs with format: ^level1,level2,level3" << endl
56     << "    Long and short level string are both accepted" << endl
57     << "    Long level string coule be: DEBUG/INFO/WARN/ERROR/FATAL." << endl
58     << "    Short level string coule be: D/I/W/E/F." << endl
59     << "    Default levels are all levels." << endl
60     << "  -D <domain>, --domain=<domain>" << endl
61     << "    Show specific domain/domains logs with format: domain1,domain2,doman3" << endl
62     << "    Don't show specific domain/domains logs with format: ^domain1,domain2,doman3" << endl
63     << "    Max domain count is " << MAX_DOMAINS << "." << endl
64     << "    See domain description at the end of this message." << endl
65     << "  -T <tag>, --tag=<tag>" << endl
66     << "    Show specific tag/tags logs with format: tag1,tag2,tag3" << endl
67     << "    Don't show specific tag/tags logs with format: ^tag1,tag2,tag3" << endl
68     << "    Max tag count is " << MAX_TAGS << "." << endl
69     << "  -P <pid>, --pid=<pid>" << endl
70     << "    Show specific pid/pids logs with format: pid1,pid2,pid3" << endl
71     << "    Don't show specific domain/domains logs with format: ^pid1,pid2,pid3" << endl
72     << "    Max pid count is " << MAX_PIDS << "." << endl
73     << "  -e <expr>, --regex=<expr>" << endl
74     << "    Show the logs which match the regular expression <expr>." << endl
75     << "  -v <format>, --format=<format>" << endl
76     << "    Show logs in different formats, options are:" << endl
77     << "      color or colour      display colorful logs by log level.i.e." << endl
78     << "        \x1B[38;5;75mDEBUG        \x1B[38;5;40mINFO        \x1B[38;5;166mWARN"
79     << "        \x1B[38;5;196mERROR       \x1B[38;5;226mFATAL\x1B[0m" << endl
80     << "      time format options are(single accepted):" << endl
81     << "        time       display local time, this is default." << endl
82     << "        epoch      display the time from 1970/1/1." << endl
83     << "        monotonic  display the cpu time from bootup." << endl
84     << "      time accuracy format options are(single accepted):" << endl
85     << "        msec       display time by millisecond, this is default." << endl
86     << "        usec       display time by microsecond." << endl
87     << "        nsec       display time by nanosecond." << endl
88     << "      year       display the year when -v time is specified." << endl
89     << "      zone       display the time zone when -v time is specified." << endl
90     << "    Different types of formats can be combined, such as:" << endl
91     << "    -v color -v time -v msec -v year -v zone." << endl;
92 }
93 
ClearHelper()94 static void ClearHelper()
95 {
96     cout
97     << "-r" << endl
98     << "  Remove all logs in hilogd buffer, advanced option:" << endl
99     << "  -t <type>, --type=<type>" << endl
100     << "    Remove specific type/types logs in buffer with format: type1,type2,type3" << endl
101     << "    Type coule be: app/core/init/kmsg." << endl
102     << "    Default types are: app,core" << endl;
103 }
104 
BufferHelper()105 static void BufferHelper()
106 {
107     cout
108     << "-g" << endl
109     << "  Query hilogd buffer size, advanced option:" << endl
110     << "  -t <type>, --type=<type>" << endl
111     << "    Query specific type/types buffer size with format: type1,type2,type3" << endl
112     << "    Type coule be: app/core/init/kmsg." << endl
113     << "    Default types are: app,core" << endl
114     << "-G <size>, --buffer-size=<size>" << endl
115     << "  Set hilogd buffer size, <size> could be number or number with unit." << endl
116     << "  Unit could be: B/K/M/G which represents Byte/Kilobyte/Megabyte/Gigabyte." << endl
117     << "  <size> range: [" << Size2Str(MIN_BUFFER_SIZE) << "," << Size2Str(MIN_BUFFER_SIZE) << "]." << endl
118     << "  Advanced option:" << endl
119     << "  -t <type>, --type=<type>" << endl
120     << "    Set specific type/types log buffer size with format: type1,type2,type3" << endl
121     << "    Type coule be: app/core/init/kmsg." << endl
122     << "    Default types are: app,core" << endl
123     << "  **It's a persistant configuration**" << endl;
124 }
125 
StatisticHelper()126 static void StatisticHelper()
127 {
128     cout
129     << "-s, --statistics" << endl
130     << "  Query log statistic information." << endl
131     << "  Set param persist.sys.hilog.stats true to enable statistic." << endl
132     << "  Set param persist.sys.hilog.stats.tag true to enable statistic of log tag." << endl
133     << "-S" << endl
134     << "  Clear hilogd statistic information." << endl;
135 }
136 
PersistTaskHelper()137 static void PersistTaskHelper()
138 {
139     cout
140     << "-w <control>,--write=<control>" << endl
141     << "  Log persistance task control, options are:" << endl
142     << "    query      query tasks informations" << endl
143     << "    stop       stop all tasks" << endl
144     << "    start      start one task" << endl
145     << "  Persistance task is used for saving logs in files." << endl
146     << "  The files are saved in directory: " << HILOG_FILE_DIR << endl
147     << "  Advanced options:" << endl
148     << "  -f <filename>, --filename=<filename>" << endl
149     << "    Set log file name, name should be valid of Linux FS." << endl
150     << "  -l <length>, --length=<length>" << endl
151     << "    Set single log file size. <length> could be number or number with unit." << endl
152     << "    Unit could be: B/K/M/G which represents Byte/Kilobyte/Megabyte/Gigabyte." << endl
153     << "    <length> range: [" << Size2Str(MIN_LOG_FILE_SIZE) <<", " << Size2Str(MAX_LOG_FILE_SIZE) << "]." << endl
154     << "  -n <number>, --number<number>" << endl
155     << "    Set max log file numbers, log file rotate when files count over this number." << endl
156     << "    <number> range: [" << MIN_LOG_FILE_NUM << ", " << MAX_LOG_FILE_NUM << "]." << endl
157     << "  -m <compress algorithm>,--stream=<compress algorithm>" << endl
158     << "    Set log file compressed algorithm, options are:" << endl
159     << "      none       write file with non-compressed logs." << endl
160     << "      zlib       write file with zlib compressed logs." << endl
161     << "  -j <jobid>, --jobid<jobid>" << endl
162     << "    Start/stop specific task of <jobid>." << endl
163     << "    <jobid> range: [" << JOB_ID_MIN << ", 0x" << hex << JOB_ID_MAX << dec << ")." << endl
164     << "  User can start task with options (t/L/D/T/P/e/v) as if using them when \"Query logs\" too." << endl
165     << "  **It's a persistant configuration**" << endl;
166 }
167 
PrivateHelper()168 static void PrivateHelper()
169 {
170     cout
171     << "-p <on/off>, --privacy <on/off>" << endl
172     << "  Set HILOG api privacy formatter feature on or off." << endl
173     << "  **It's a temporary configuration, will be lost after reboot**" << endl;
174 }
175 
KmsgHelper()176 static void KmsgHelper()
177 {
178     cout
179     << "-k <on/off>, --kmsg <on/off>" << endl
180     << "  Set hilogd storing kmsg log feature on or off" << endl
181     << "  **It's a persistant configuration**" << endl;
182 }
183 
FlowControlHelper()184 static void FlowControlHelper()
185 {
186     cout
187     << "-Q <control-type>" << endl
188     << "  Set log flow-control feature on or off, options are:" << endl
189     << "    pidon     process flow control on" << endl
190     << "    pidoff    process flow control off" << endl
191     << "    domainon  domain flow control on" << endl
192     << "    domainoff domain flow control off" << endl
193     << "  **It's a temporary configuration, will be lost after reboot**" << endl;
194 }
195 
BaseLevelHelper()196 static void BaseLevelHelper()
197 {
198     cout
199     << "-b <loglevel>, --baselevel=<loglevel>" << endl
200     << "  Set global loggable level to <loglevel>" << endl
201     << "  Long and short level string are both accepted." << endl
202     << "  Long level string coule be: DEBUG/INFO/WARN/ERROR/FATAL/X." << endl
203     << "  Short level string coule be: D/I/W/E/F/X." << endl
204     << "  X means that loggable level is higher than the max level, no log could be printed." << endl
205     << "  Advanced options:" << endl
206     << "  -D <domain>, --domain=<domain>" << endl
207     << "    Set specific domain loggable level." << endl
208     << "    See domain description at the end of this message." << endl
209     << "  -T <tag>, --tag=<tag>" << endl
210     << "    Set specific tag loggable level." << endl
211     << "    The priority is: tag level > domain level > global level." << endl
212     << "  **It's a temporary configuration, will be lost after reboot**" << endl;
213 }
214 
DomainHelper()215 static void DomainHelper()
216 {
217     cout
218     << endl << endl
219     << "Domain description:" << endl
220     << "  Log type \"core\" & \"init\" are used for OS subsystems, the range is [0x" << hex << DOMAIN_OS_MIN << ","
221     << "  0x" << DOMAIN_OS_MAX << "]" << endl
222     << "  Log type \"app\" is used for applications, the range is [0x" << DOMAIN_APP_MIN << ","
223     << "  0x" << DOMAIN_APP_MAX << "]" << dec << endl
224     << "  To reduce redundant info when printing logs, only last five hex numbers of domain are printed" << endl
225     << "  So if user wants to use -D option to filter OS logs, user should add 0xD0 as prefix to the printed domain:"
226     << endl
227     << "  Exapmle: hilog -D 0xD0xxxxx" << endl
228     << "  The xxxxx is the domain string printed in logs." << endl;
229 }
230 
ComboHelper()231 static void ComboHelper()
232 {
233     cout
234     << "The first layer options can't be used in combination, ILLEGAL expamples:" << endl
235     << "    hilog -S -s; hilog -w start -r; hilog -p on -k on -b D" << endl;
236 }
237 
238 using HelperFunc = std::function<void()>;
239 static const std::list<pair<string, HelperFunc>> g_HelperList = {
240     {"query", QueryHelper},
241     {"clear", ClearHelper},
242     {"buffer", BufferHelper},
243     {"stats", StatisticHelper},
244     {"persist", PersistTaskHelper},
245     {"private", PrivateHelper},
246     {"kmsg", KmsgHelper},
247     {"flowcontrol", FlowControlHelper},
248     {"baselevel", BaseLevelHelper},
249     {"combo", ComboHelper},
250     {"domain", DomainHelper},
251 };
252 
Helper(const string & arg)253 static void Helper(const string& arg)
254 {
255     cout << "Usage:" << endl
256     << "-h --help" << endl
257     << "  Show all help information." << endl
258     << "  Show single help information with option: " << endl
259     << "  query/clear/buffer/stats/persist/private/kmsg/flowcontrol/baselevel/domain/combo" << endl;
260     for (auto &it : g_HelperList) {
261         if (arg == "" || arg == it.first) {
262             it.second();
263         }
264     }
265     return;
266 }
267 
PrintErr(int error)268 static void PrintErr(int error)
269 {
270     cerr << ErrorCode2Str(error) << endl;
271 }
272 
PrintResult(int ret,const string & msg)273 static void PrintResult(int ret, const string& msg)
274 {
275     cout << msg << ((ret == RET_SUCCESS) ? " successfully" : " failed") << endl;
276 }
277 
278 enum class ControlCmd {
279     NOT_CMD = -1,
280     CMD_HELP = 0,
281     CMD_QUERY,
282     CMD_REMOVE,
283     CMD_BUFFER_SIZE_QUERY,
284     CMD_BUFFER_SIZE_SET,
285     CMD_STATS_INFO_QUERY,
286     CMD_STATS_INFO_CLEAR,
287     CMD_PERSIST_TASK,
288     CMD_PRIVATE_FEATURE_SET,
289     CMD_KMSG_FEATURE_SET,
290     CMD_FLOWCONTROL_FEATURE_SET,
291     CMD_LOGLEVEL_SET,
292 };
293 
294 struct HilogArgs {
295     uint16_t headLines;
296     uint16_t baseLevel;
297     bool blackDomain;
298     int domainCount;
299     uint32_t domains[MAX_DOMAINS];
300     string regex;
301     string fileName;
302     int32_t buffSize;
303     uint32_t jobId;
304     uint32_t fileSize;
305     uint16_t levels;
306     string stream;
307     uint16_t fileNum;
308     bool blackPid;
309     int pidCount;
310     uint32_t pids[MAX_PIDS];
311     uint16_t types;
312     bool blackTag;
313     int tagCount;
314     string tags[MAX_TAGS];
315     bool colorful;
316     FormatTime timeFormat;
317     FormatTimeAccu timeAccuFormat;
318     bool year;
319     bool zone;
320     bool noBlock;
321     uint16_t tailLines;
322 
HilogArgsOHOS::HiviewDFX::HilogArgs323     HilogArgs() : headLines (0), baseLevel(0), blackDomain(false), domainCount(0), domains { 0 }, regex(""),
324         fileName(""), buffSize(0), jobId(0), fileSize(0), levels(0), stream(""), fileNum(0), blackPid(false),
325         pidCount(0), pids { 0 }, types(0), blackTag(false), tagCount(0), tags { "" }, colorful(false),
326         timeFormat(FormatTime::INVALID), timeAccuFormat(FormatTimeAccu::INVALID), year(false), zone(false),
327         noBlock(false), tailLines(0) {}
328 
ToOutputRqstOHOS::HiviewDFX::HilogArgs329     void ToOutputRqst(OutputRqst& rqst)
330     {
331         rqst.headLines = headLines;
332         rqst.types = types;
333         rqst.levels = levels;
334         rqst.blackDomain = blackDomain;
335         rqst.domainCount = static_cast<uint8_t>(domainCount);
336         int i;
337         for (i = 0; i < domainCount; i++) {
338             rqst.domains[i] = domains[i];
339         }
340         rqst.blackTag = blackTag;
341         rqst.tagCount = tagCount;
342         for (i = 0; i < tagCount; i++) {
343             (void)strncpy_s(rqst.tags[i], MAX_TAG_LEN, tags[i].c_str(), tags[i].length());
344         }
345         rqst.blackPid = blackPid;
346         rqst.pidCount = pidCount;
347         for (i = 0; i < pidCount; i++) {
348             rqst.pids[i] = pids[i];
349         }
350         (void)strncpy_s(rqst.regex, MAX_REGEX_STR_LEN, regex.c_str(), regex.length());
351         rqst.noBlock = noBlock;
352         rqst.tailLines = tailLines;
353     }
354 
ToPersistStartRqstOHOS::HiviewDFX::HilogArgs355     void ToPersistStartRqst(PersistStartRqst& rqst)
356     {
357         ToOutputRqst(rqst.outputFilter);
358         rqst.jobId = jobId;
359         rqst.fileNum = fileNum;
360         rqst.fileSize = fileSize;
361         (void)strncpy_s(rqst.fileName, MAX_FILE_NAME_LEN, fileName.c_str(), fileName.length());
362         (void)strncpy_s(rqst.stream, MAX_STREAM_NAME_LEN, stream.c_str(), stream.length());
363     }
364 
ToPersistStopRqstOHOS::HiviewDFX::HilogArgs365     void ToPersistStopRqst(PersistStopRqst& rqst)
366     {
367         rqst.jobId = jobId;
368     }
369 
ToBufferSizeSetRqstOHOS::HiviewDFX::HilogArgs370     void ToBufferSizeSetRqst(BufferSizeSetRqst& rqst)
371     {
372         rqst.types = types;
373         rqst.size = buffSize;
374     }
375 
ToBufferSizeGetRqstOHOS::HiviewDFX::HilogArgs376     void ToBufferSizeGetRqst(BufferSizeGetRqst& rqst)
377     {
378         rqst.types = types;
379     }
380 
ToStatsQueryRqstOHOS::HiviewDFX::HilogArgs381     void ToStatsQueryRqst(StatsQueryRqst& rqst)
382     {
383         rqst.types = types;
384         rqst.domainCount = static_cast<uint8_t>(domainCount);
385         int i;
386         for (i = 0; i < domainCount; i++) {
387             rqst.domains[i] = domains[i];
388         }
389     }
390 
ToLogRemoveRqstOHOS::HiviewDFX::HilogArgs391     void ToLogRemoveRqst(LogRemoveRqst& rqst)
392     {
393         rqst.types = types;
394     }
395 };
396 
397 using OptHandler = std::function<int(HilogArgs& context, const char *arg)>;
398 
QueryLogHandler(HilogArgs & context,const char * arg)399 static int QueryLogHandler(HilogArgs& context, const char *arg)
400 {
401     OutputRqst rqst = { 0 };
402     context.ToOutputRqst(rqst);
403     LogIoctl ioctl(IoctlCmd::OUTPUT_RQST, IoctlCmd::OUTPUT_RSP);
404     int ret = ioctl.RequestOutput(rqst, [&context](const OutputRsp& rsp) {
405         if (rsp.end) {
406             return RET_SUCCESS;
407         }
408         LogContent content = {
409             .level = rsp.level,
410             .type = rsp.type,
411             .pid = rsp.pid,
412             .tid = rsp.tid,
413             .domain = rsp.domain,
414             .tv_sec = rsp.tv_sec,
415             .tv_nsec = rsp.tv_nsec,
416             .mono_sec = rsp.mono_sec,
417             .tag = rsp.data,
418             .log = (rsp.data + rsp.tagLen)
419         };
420         LogFormat format = {
421             .colorful = context.colorful,
422             .timeFormat = ((context.timeFormat == FormatTime::INVALID) ? FormatTime::TIME : context.timeFormat),
423             .timeAccuFormat =
424                 ((context.timeAccuFormat == FormatTimeAccu::INVALID) ? FormatTimeAccu::MSEC : context.timeAccuFormat),
425             .year = context.year,
426             .zone = context.zone
427         };
428         LogPrintWithFormat(content, format);
429         return static_cast<int>(SUCCESS_CONTINUE);
430     });
431     if (ret != RET_SUCCESS) {
432         return ret;
433     }
434     return RET_SUCCESS;
435 }
436 
HeadHandler(HilogArgs & context,const char * arg)437 static int HeadHandler(HilogArgs& context, const char *arg)
438 {
439     if (IsNumericStr(arg) == false) {
440         return ERR_NOT_NUMBER_STR;
441     }
442     int lines = 0;
443     (void)StrToInt(arg, lines);
444     context.headLines = static_cast<uint16_t>(lines);
445     context.noBlock = true; // don't block implicitly
446     return QueryLogHandler(context, arg);
447 }
448 
BaseLogLevelHandler(HilogArgs & context,const char * arg)449 static int BaseLogLevelHandler(HilogArgs& context, const char *arg)
450 {
451     uint16_t baseLevel = PrettyStr2LogLevel(arg);
452     if (baseLevel == LOG_LEVEL_MIN) {
453         return ERR_LOG_LEVEL_INVALID;
454     }
455     context.baseLevel = baseLevel;
456     int ret;
457     if (context.domainCount == 0 && context.tagCount == 0) {
458         ret = SetGlobalLevel(context.baseLevel);
459         PrintResult(ret, (string("Set global log level to ") + arg));
460     }
461     if (context.domainCount != 0) {
462         for (int i = 0; i < context.domainCount; i++) {
463             ret = SetDomainLevel(context.domains[i], context.baseLevel);
464             PrintResult(ret, (string("Set domain 0x") + Uint2HexStr(context.domains[i]) +  " log level to " + arg));
465         }
466     }
467     if (context.tagCount != 0) {
468         for (int i = 0; i < context.tagCount; i++) {
469             ret = SetTagLevel(context.tags[i], context.baseLevel);
470             PrintResult(ret, (string("Set tag ") + context.tags[i] +  " log level to " + arg));
471         }
472     }
473     return RET_SUCCESS;
474 }
475 
476 static constexpr char BLACK_PREFIX = '^';
DomainHandler(HilogArgs & context,const char * arg)477 static int DomainHandler(HilogArgs& context, const char *arg)
478 {
479     context.blackDomain = (arg[0] == BLACK_PREFIX);
480     std::vector<std::string> domains;
481     Split(context.blackDomain ? arg + 1 : arg, domains);
482     if (domains.size() == 0) {
483         return ERR_INVALID_ARGUMENT;
484     }
485     int index = 0;
486     for (string d : domains) {
487         if (index >= MAX_DOMAINS) {
488             return ERR_TOO_MANY_DOMAINS;
489         }
490         uint32_t domain = HexStr2Uint(d);
491         if (domain == 0) {
492             return ERR_INVALID_DOMAIN_STR;
493         }
494         context.domains[index++] = domain;
495     }
496     context.domainCount = index;
497     return RET_SUCCESS;
498 }
499 
RegexHandler(HilogArgs & context,const char * arg)500 static int RegexHandler(HilogArgs& context, const char *arg)
501 {
502     context.regex = arg;
503     if (context.regex.length() >= MAX_REGEX_STR_LEN) {
504         return ERR_REGEX_STR_TOO_LONG;
505     }
506     return RET_SUCCESS;
507 }
508 
FileNameHandler(HilogArgs & context,const char * arg)509 static int FileNameHandler(HilogArgs& context, const char *arg)
510 {
511     context.fileName = arg;
512     if (context.fileName.length() >= MAX_FILE_NAME_LEN) {
513         return ERR_FILE_NAME_TOO_LONG;
514     }
515     return RET_SUCCESS;
516 }
517 
BufferSizeGetHandler(HilogArgs & context,const char * arg)518 static int BufferSizeGetHandler(HilogArgs& context, const char *arg)
519 {
520     BufferSizeGetRqst rqst = { 0 };
521     context.ToBufferSizeGetRqst(rqst);
522     LogIoctl ioctl(IoctlCmd::BUFFERSIZE_GET_RQST, IoctlCmd::BUFFERSIZE_GET_RSP);
523     int ret = ioctl.Request<BufferSizeGetRqst, BufferSizeGetRsp>(rqst, [&rqst](const BufferSizeGetRsp& rsp) {
524         for (uint16_t i = 0; i < static_cast<uint16_t>(LOG_TYPE_MAX); i++) {
525             if (rsp.size[i] > 0) {
526                 cout << "Log type " << LogType2Str(i) << " buffer size is " << Size2Str(rsp.size[i]) << endl;
527             }
528         }
529         return RET_SUCCESS;
530     });
531     if (ret != RET_SUCCESS) {
532         cout << "Get " << ComboLogType2Str(rqst.types) << " buffer size failed" << endl;
533     }
534     return ret;
535 }
536 
BufferSizeSetHandler(HilogArgs & context,const char * arg)537 static int BufferSizeSetHandler(HilogArgs& context, const char *arg)
538 {
539     uint64_t size = Str2Size(arg);
540     if (size == 0) {
541         return ERR_INVALID_SIZE_STR;
542     }
543     context.buffSize = static_cast<int32_t>(size);
544     BufferSizeSetRqst rqst;
545     context.ToBufferSizeSetRqst(rqst);
546     LogIoctl ioctl(IoctlCmd::BUFFERSIZE_SET_RQST, IoctlCmd::BUFFERSIZE_SET_RSP);
547     int ret = ioctl.Request<BufferSizeSetRqst, BufferSizeSetRsp>(rqst, [&rqst](const BufferSizeSetRsp& rsp) {
548         for (uint16_t i = 0; i < static_cast<uint16_t>(LOG_TYPE_MAX); i++) {
549             if (rsp.size[i] > 0) {
550                 cout << "Set log type " << LogType2Str(i) << " buffer size to "
551                 << Size2Str(rsp.size[i]) << " successfully" << endl;
552             } else if (rsp.size[i] < 0) {
553                 cout << "Set log type " << LogType2Str(i) << " buffer size to "
554                 << Size2Str(rqst.size) << " failed" << endl;
555                 PrintErr(rsp.size[i]);
556             }
557         }
558         return RET_SUCCESS;
559     });
560     if (ret != RET_SUCCESS) {
561         cout << "Set buffer size failed" << endl;
562     }
563     return ret;
564 }
565 
HelpHandler(HilogArgs & context,const char * arg)566 static int HelpHandler(HilogArgs& context, const char *arg)
567 {
568     Helper("");
569     return RET_SUCCESS;
570 }
571 
JobIdHandler(HilogArgs & context,const char * arg)572 static int JobIdHandler(HilogArgs& context, const char *arg)
573 {
574     if (IsNumericStr(arg) == false) {
575         return ERR_NOT_NUMBER_STR;
576     }
577     int jobId = 0;
578     (void)StrToInt(arg, jobId);
579     context.jobId = static_cast<uint32_t>(jobId);
580     return RET_SUCCESS;
581 }
582 
583 static const string FEATURE_ON = "on";
584 static const string FEATURE_OFF = "off";
KmsgFeatureSetHandler(HilogArgs & context,const char * arg)585 static int KmsgFeatureSetHandler(HilogArgs& context, const char *arg)
586 {
587     string argStr = arg;
588     bool kmsgOn = true;
589     if (argStr == FEATURE_ON) {
590         kmsgOn = true;
591     } else if (argStr == FEATURE_OFF) {
592         kmsgOn = false;
593     } else {
594         return ERR_INVALID_ARGUMENT;
595     }
596     KmsgEnableRqst rqst = { kmsgOn };
597     LogIoctl ioctl(IoctlCmd::KMSG_ENABLE_RQST, IoctlCmd::KMSG_ENABLE_RSP);
598     int ret = ioctl.Request<KmsgEnableRqst, KmsgEnableRsp>(rqst, [&rqst](const KmsgEnableRsp& rsp) {
599         return RET_SUCCESS;
600     });
601     PrintResult(ret, (string("Set hilogd storing kmsg log ") + arg));
602     return ret;
603 }
604 
FileLengthHandler(HilogArgs & context,const char * arg)605 static int FileLengthHandler(HilogArgs& context, const char *arg)
606 {
607     uint64_t size = Str2Size(arg);
608     if (size == 0) {
609         return ERR_INVALID_SIZE_STR;
610     }
611     context.fileSize = size;
612     return RET_SUCCESS;
613 }
614 
LevelHandler(HilogArgs & context,const char * arg)615 static int LevelHandler(HilogArgs& context, const char *arg)
616 {
617     uint16_t levels = Str2ComboLogLevel(arg);
618     if (levels == 0) {
619         return ERR_LOG_LEVEL_INVALID;
620     }
621     context.levels = levels;
622     return RET_SUCCESS;
623 }
624 
FileCompressHandler(HilogArgs & context,const char * arg)625 static int FileCompressHandler(HilogArgs& context, const char *arg)
626 {
627     context.stream = arg;
628     return RET_SUCCESS;
629 }
630 
FileNumberHandler(HilogArgs & context,const char * arg)631 static int FileNumberHandler(HilogArgs& context, const char *arg)
632 {
633     if (IsNumericStr(arg) == false) {
634         return ERR_NOT_NUMBER_STR;
635     }
636     int fileNum = 0;
637     (void)StrToInt(arg, fileNum);
638     context.fileNum = static_cast<uint16_t>(fileNum);
639     return RET_SUCCESS;
640 }
641 
PrivateFeatureSetHandler(HilogArgs & context,const char * arg)642 static int PrivateFeatureSetHandler(HilogArgs& context, const char *arg)
643 {
644     string argStr = arg;
645     bool privateOn = true;
646     if (argStr == FEATURE_ON) {
647         privateOn = true;
648     } else if (argStr == FEATURE_OFF) {
649         privateOn = false;
650     } else {
651         return ERR_INVALID_ARGUMENT;
652     }
653     int ret = SetPrivateSwitchOn(privateOn);
654     PrintResult(ret, (string("Set hilog privacy format ") + arg));
655     return RET_SUCCESS;
656 }
657 
PidHandler(HilogArgs & context,const char * arg)658 static int PidHandler(HilogArgs& context, const char *arg)
659 {
660     context.blackPid = (arg[0] == BLACK_PREFIX);
661     std::vector<std::string> pids;
662     Split(context.blackPid ? arg + 1 : arg, pids);
663     if (pids.size() == 0) {
664         return ERR_INVALID_ARGUMENT;
665     }
666     int index = 0;
667     for (string p : pids) {
668         if (index >= MAX_PIDS) {
669             return ERR_TOO_MANY_PIDS;
670         }
671         if (IsNumericStr(p) == false) {
672             return ERR_NOT_NUMBER_STR;
673         }
674         int pid = 0;
675         (void)StrToInt(p, pid);
676         context.pids[index++] = static_cast<uint32_t>(pid);
677     }
678     context.pidCount = index;
679     return RET_SUCCESS;
680 }
681 
SetDomainFlowCtrl(bool on)682 static int SetDomainFlowCtrl(bool on)
683 {
684     DomainFlowCtrlRqst rqst = { 0 };
685     rqst.on = on;
686     LogIoctl ioctl(IoctlCmd::DOMAIN_FLOWCTRL_RQST, IoctlCmd::DOMAIN_FLOWCTRL_RSP);
687     int ret = ioctl.Request<DomainFlowCtrlRqst, DomainFlowCtrlRsp>(rqst, [&rqst](const DomainFlowCtrlRsp& rsp) {
688         return RET_SUCCESS;
689     });
690     return ret;
691 }
FlowControlFeatureSetHandler(HilogArgs & context,const char * arg)692 static int FlowControlFeatureSetHandler(HilogArgs& context, const char *arg)
693 {
694     string pid = "pid";
695     string domain = "domain";
696     string argStr = arg;
697     int ret;
698     if (argStr == (pid + FEATURE_ON)) {
699         ret = SetProcessSwitchOn(true);
700         cout << "Set flow control by process to enabled, result: " << ErrorCode2Str(ret) << endl;
701     } else if (argStr == (pid + FEATURE_OFF)) {
702         ret = SetProcessSwitchOn(false);
703         cout << "Set flow control by process to disabled, result: " << ErrorCode2Str(ret) << endl;
704     } else if (argStr == (domain + FEATURE_ON)) {
705         ret = SetDomainFlowCtrl(true);
706         cout << "Set flow control by domain to enabled, result: " << ErrorCode2Str(ret) << endl;
707     } else if (argStr == (domain + FEATURE_OFF)) {
708         ret = SetDomainFlowCtrl(false);
709         cout << "Set flow control by domain to disabled, result: " << ErrorCode2Str(ret) << endl;
710     } else {
711         return ERR_INVALID_ARGUMENT;
712     }
713     return ret;
714 }
715 
RemoveHandler(HilogArgs & context,const char * arg)716 static int RemoveHandler(HilogArgs& context, const char *arg)
717 {
718     LogRemoveRqst rqst = { 0 };
719     context.ToLogRemoveRqst(rqst);
720     LogIoctl ioctl(IoctlCmd::LOG_REMOVE_RQST, IoctlCmd::LOG_REMOVE_RSP);
721     int ret = ioctl.Request<LogRemoveRqst, LogRemoveRsp>(rqst, [&rqst](const LogRemoveRsp& rsp) {
722         cout << "Log type " << ComboLogType2Str(rsp.types) << " buffer clear successfully" << endl;
723         return RET_SUCCESS;
724     });
725     if (ret != RET_SUCCESS) {
726         cout << "Log buffer clear failed" << endl;
727     }
728     return ret;
729 }
730 
StatsInfoQueryHandler(HilogArgs & context,const char * arg)731 static int StatsInfoQueryHandler(HilogArgs& context, const char *arg)
732 {
733     StatsQueryRqst rqst = { 0 };
734     context.ToStatsQueryRqst(rqst);
735     LogIoctl ioctl(IoctlCmd::STATS_QUERY_RQST, IoctlCmd::STATS_QUERY_RSP);
736     int ret = ioctl.RequestStatsQuery(rqst, [&rqst](const StatsQueryRsp& rsp) {
737         HilogShowLogStatsInfo(rsp);
738         return RET_SUCCESS;
739     });
740     if (ret != RET_SUCCESS) {
741         cout << "Statistic info query failed" << endl;
742     }
743     return ret;
744 }
745 
StatsInfoClearHandler(HilogArgs & context,const char * arg)746 static int StatsInfoClearHandler(HilogArgs& context, const char *arg)
747 {
748     StatsClearRqst rqst = { 0 };
749     LogIoctl ioctl(IoctlCmd::STATS_CLEAR_RQST, IoctlCmd::STATS_CLEAR_RSP);
750     int ret = ioctl.Request<StatsClearRqst, StatsClearRsp>(rqst, [&rqst](const StatsClearRsp& rsp) {
751         cout << "Statistic info clear successfully" << endl;
752         return RET_SUCCESS;
753     });
754     if (ret != RET_SUCCESS) {
755         cout << "Statistic info clear failed" << endl;
756     }
757     return ret;
758 }
759 
TypeHandler(HilogArgs & context,const char * arg)760 static int TypeHandler(HilogArgs& context, const char *arg)
761 {
762     uint16_t types = Str2ComboLogType(arg);
763     if (types == 0) {
764         return ERR_LOG_TYPE_INVALID;
765     }
766     context.types = types;
767     return RET_SUCCESS;
768 }
769 
TagHandler(HilogArgs & context,const char * arg)770 static int TagHandler(HilogArgs& context, const char *arg)
771 {
772     context.blackTag = (arg[0] == BLACK_PREFIX);
773     std::vector<std::string> tags;
774     Split(context.blackTag ? arg + 1 : arg, tags);
775     int index = 0;
776     for (string t : tags) {
777         if (index >= MAX_TAGS) {
778             return ERR_TOO_MANY_TAGS;
779         }
780         if (t.length() >= MAX_TAG_LEN) {
781             return ERR_TAG_STR_TOO_LONG;
782         }
783         context.tags[index++] = t;
784     }
785     context.tagCount = index;
786     return RET_SUCCESS;
787 }
788 
FormatHandler(HilogArgs & context,const char * arg)789 static int FormatHandler(HilogArgs& context, const char *arg)
790 {
791     string strArg = arg;
792     if (strArg == "color" || strArg == "colour") {
793         context.colorful = true;
794     } else if (strArg == "time" || strArg == "epoch" || strArg == "monotonic") {
795         if (context.timeFormat != FormatTime::INVALID) {
796             return ERR_DUPLICATE_OPTION;
797         }
798         if (strArg == "time") {
799             context.timeFormat = FormatTime::TIME;
800         } else if (strArg == "epoch") {
801             context.timeFormat = FormatTime::EPOCH;
802         } else if (strArg == "monotonic") {
803             context.timeFormat = FormatTime::MONOTONIC;
804         }
805     } else if (strArg == "msec" || strArg == "usec" || strArg == "nsec") {
806         if (context.timeAccuFormat != FormatTimeAccu::INVALID) {
807             return ERR_DUPLICATE_OPTION;
808         }
809         if (strArg == "msec") {
810             context.timeAccuFormat = FormatTimeAccu::MSEC;
811         } else if (strArg == "usec") {
812             context.timeAccuFormat = FormatTimeAccu::USEC;
813         } else if (strArg == "nsec") {
814             context.timeAccuFormat = FormatTimeAccu::NSEC;
815         }
816     } else if (strArg == "year") {
817         context.year = true;
818     } else if (strArg == "zone") {
819         context.zone = true;
820         tzset();
821     } else {
822         return ERR_INVALID_ARGUMENT;
823     }
824     return RET_SUCCESS;
825 }
826 
PersistTaskStart(HilogArgs & context)827 static int PersistTaskStart(HilogArgs& context)
828 {
829     PersistStartRqst rqst = { { 0 }, 0 };
830     context.ToPersistStartRqst(rqst);
831     LogIoctl ioctl(IoctlCmd::PERSIST_START_RQST, IoctlCmd::PERSIST_START_RSP);
832     int ret = ioctl.Request<PersistStartRqst, PersistStartRsp>(rqst, [&rqst](const PersistStartRsp& rsp) {
833         cout << "Persist task [jobid:" << rsp.jobId << "] start successfully" << endl;
834         return RET_SUCCESS;
835     });
836     if (ret != RET_SUCCESS) {
837         cout << "Persist task start failed" << endl;
838     }
839     return ret;
840 }
841 
PersistTaskStop(HilogArgs & context)842 static int PersistTaskStop(HilogArgs& context)
843 {
844     PersistStopRqst rqst = { 0 };
845     context.ToPersistStopRqst(rqst);
846     LogIoctl ioctl(IoctlCmd::PERSIST_STOP_RQST, IoctlCmd::PERSIST_STOP_RSP);
847     int ret = ioctl.Request<PersistStopRqst, PersistStopRsp>(rqst,  [&rqst](const PersistStopRsp& rsp) {
848         for (int i = 0; i < rsp.jobNum; i++) {
849             cout << "Persist task [jobid:" << rsp.jobId[i] << "] stop successfully" << endl;
850         }
851         return RET_SUCCESS;
852     });
853     if (ret != RET_SUCCESS) {
854         cout << "Persist task stop failed" << endl;
855     }
856     return ret;
857 }
858 
PrintTaskInfo(const PersistTaskInfo & task)859 static void PrintTaskInfo(const PersistTaskInfo& task)
860 {
861     cout << task.jobId << " " << ComboLogType2Str(task.outputFilter.types) << " " << task.stream << " ";
862     cout << task.fileName << " " << Size2Str(task.fileSize) << " " << to_string(task.fileNum) << endl;
863 }
864 
PersistTaskQuery()865 static int PersistTaskQuery()
866 {
867     PersistQueryRqst rqst = { 0 };
868     LogIoctl ioctl(IoctlCmd::PERSIST_QUERY_RQST, IoctlCmd::PERSIST_QUERY_RSP);
869     int ret = ioctl.Request<PersistQueryRqst, PersistQueryRsp>(rqst,  [&rqst](const PersistQueryRsp& rsp) {
870         for (int i = 0; i < rsp.jobNum; i++) {
871             PrintTaskInfo(rsp.taskInfo[i]);
872         }
873         return RET_SUCCESS;
874     });
875     if (ret != RET_SUCCESS) {
876         cout << "Persist task query failed" << endl;
877     }
878     return ret;
879 }
880 
PersistTaskHandler(HilogArgs & context,const char * arg)881 static int PersistTaskHandler(HilogArgs& context, const char *arg)
882 {
883     string strArg = arg;
884     if (strArg == "start") {
885         return PersistTaskStart(context);
886     } else if (strArg == "stop") {
887         return PersistTaskStop(context);
888     } else if (strArg == "query") {
889         return PersistTaskQuery();
890     } else {
891         return ERR_INVALID_ARGUMENT;
892     }
893     return RET_SUCCESS;
894 }
895 
NoBlockHandler(HilogArgs & context,const char * arg)896 static int NoBlockHandler(HilogArgs& context, const char *arg)
897 {
898     context.noBlock = true;
899     return QueryLogHandler(context, arg);
900 }
901 
TailHandler(HilogArgs & context,const char * arg)902 static int TailHandler(HilogArgs& context, const char *arg)
903 {
904     if (IsNumericStr(arg) == false) {
905         return ERR_NOT_NUMBER_STR;
906     }
907     int tailLines = 0;
908     (void)StrToInt(arg, tailLines);
909     context.tailLines = static_cast<uint16_t>(tailLines);
910     context.noBlock = true; // don't block implicitly
911     return QueryLogHandler(context, arg);
912 }
913 
914 struct OptEntry {
915     const char opt;
916     const char *longOpt;
917     const ControlCmd cmd;
918     const OptHandler handler;
919     const bool needArg;
920     // how many times can this option be used, for example:
921     //   hilog -v msec -v color ...
922     uint32_t count;
923 };
924 static OptEntry optEntries[] = {
925     {'a', "head", ControlCmd::CMD_QUERY, HeadHandler, true, 1},
926     {'b', "baselevel", ControlCmd::CMD_LOGLEVEL_SET, BaseLogLevelHandler, true, 1},
927     {'D', "domain", ControlCmd::NOT_CMD, DomainHandler, true, 1},
928     {'e', "regex", ControlCmd::NOT_CMD, RegexHandler, true, 1},
929     {'f', "filename", ControlCmd::NOT_CMD, FileNameHandler, true, 1},
930     {'g', nullptr, ControlCmd::CMD_BUFFER_SIZE_QUERY, BufferSizeGetHandler, false, 1},
931     {'G', "buffer-size", ControlCmd::CMD_BUFFER_SIZE_SET, BufferSizeSetHandler, true, 1},
932     {'h', "help", ControlCmd::CMD_HELP, HelpHandler, false, 1},
933     {'j', "jobid", ControlCmd::NOT_CMD, JobIdHandler, true, 1},
934     {'k', "kmsg", ControlCmd::CMD_KMSG_FEATURE_SET, KmsgFeatureSetHandler, true, 1},
935     {'l', "length", ControlCmd::NOT_CMD, FileLengthHandler, true, 1},
936     {'L', "level", ControlCmd::NOT_CMD, LevelHandler, true, 1},
937     {'m', "stream", ControlCmd::NOT_CMD, FileCompressHandler, true, 1},
938     {'n', "number", ControlCmd::NOT_CMD, FileNumberHandler, true, 1},
939     {'p', "private", ControlCmd::CMD_PRIVATE_FEATURE_SET, PrivateFeatureSetHandler, true, 1},
940     {'P', "pid", ControlCmd::NOT_CMD, PidHandler, true, 1},
941     {'Q', "flowctrl", ControlCmd::CMD_FLOWCONTROL_FEATURE_SET, FlowControlFeatureSetHandler, true, 1},
942     {'r', nullptr, ControlCmd::CMD_REMOVE, RemoveHandler, false, 1},
943     {'s', "statistics", ControlCmd::CMD_STATS_INFO_QUERY, StatsInfoQueryHandler, false, 1},
944     {'S', nullptr, ControlCmd::CMD_STATS_INFO_CLEAR, StatsInfoClearHandler, false, 1},
945     {'t', "type", ControlCmd::NOT_CMD, TypeHandler, true, 1},
946     {'T', "tag", ControlCmd::NOT_CMD, TagHandler, true, 1},
947     {'v', "format", ControlCmd::NOT_CMD, FormatHandler, true, 5},
948     {'w', "write", ControlCmd::CMD_PERSIST_TASK, PersistTaskHandler, true, 1},
949     {'x', "exit", ControlCmd::CMD_QUERY, NoBlockHandler, false, 1},
950     {'z', "tail", ControlCmd::CMD_QUERY, TailHandler, true, 1},
951     {0, nullptr, ControlCmd::NOT_CMD, nullptr, false, 1}, // End default entry
952 }; // "hxz:grsSa:v:e:t:L:G:f:l:n:j:w:p:k:D:T:b:Q:m:P:"
953 static constexpr int OPT_ENTRY_CNT = sizeof(optEntries) / sizeof(OptEntry);
954 
GetOpts(string & opts,struct option (& longOptions)[OPT_ENTRY_CNT])955 static void GetOpts(string& opts, struct option(&longOptions)[OPT_ENTRY_CNT])
956 {
957     int longOptcount = 0;
958     opts = "";
959     int i;
960     for (i = 0; i < OPT_ENTRY_CNT; i++) {
961         if (optEntries[i].opt == 0) {
962             break;
963         }
964         // opts
965         opts += optEntries[i].opt;
966         if (optEntries[i].needArg) {
967             opts += ':';
968         }
969         // long option
970         if (optEntries[i].longOpt == nullptr) {
971             continue;
972         }
973         longOptions[longOptcount].name = optEntries[i].longOpt;
974         longOptions[longOptcount].has_arg = optEntries[i].needArg ? required_argument : no_argument;
975         longOptions[longOptcount].flag = nullptr;
976         longOptions[longOptcount].val = optEntries[i].opt;
977         longOptcount++;
978     }
979     longOptions[longOptcount].name = nullptr;
980     longOptions[longOptcount].has_arg = 0;
981     longOptions[longOptcount].flag = nullptr;
982     longOptions[longOptcount].val = 0;
983     return;
984 }
985 
GetOptEntry(int choice)986 static OptEntry* GetOptEntry(int choice)
987 {
988     OptEntry *entry = &(optEntries[OPT_ENTRY_CNT - 1]);
989     int i = 0;
990     for (i = 0; i < OPT_ENTRY_CNT; i++) {
991         if (optEntries[i].opt == static_cast<char>(choice)) {
992             entry = &(optEntries[i]);
993             break;
994         }
995     }
996     return entry;
997 }
998 
HilogEntry(int argc,char * argv[])999 int HilogEntry(int argc, char* argv[])
1000 {
1001     struct option longOptions[OPT_ENTRY_CNT];
1002     string opts;
1003     GetOpts(opts, longOptions);
1004     HilogArgs context;
1005     int optIndex = 0;
1006 
1007     // 0. help has special case
1008     static const int argCountHelp = 3;
1009     if (argc == argCountHelp) {
1010         string arg = argv[1];
1011         if (arg == "--help" || arg == "-h") {
1012             Helper(argv[argCountHelp - 1]);
1013             return RET_SUCCESS;
1014         }
1015     }
1016     // 1. Scan all options and process NOT_CMD options' arguments
1017     int cmdCount = 0;
1018     OptEntry queryEntry =  {' ', "", ControlCmd::CMD_QUERY, QueryLogHandler, false, 0};
1019     OptEntry *cmdEntry = &queryEntry; // No cmd means CMD_QUERY cmd
1020     string cmdArgs = "";
1021     while (1) {
1022         int choice = getopt_long(argc, argv, opts.c_str(), longOptions, &optIndex);
1023         if (choice == -1) {
1024             break;
1025         }
1026         if (choice == '?') {
1027             return RET_FAIL;
1028         }
1029         OptEntry *entry = GetOptEntry(choice);
1030         if (optind < argc && argv[optind][0] != '-') { // all options need only 1 argument
1031             PrintErr(ERR_TOO_MANY_ARGUMENTS);
1032             return ERR_TOO_MANY_ARGUMENTS;
1033         }
1034         if (entry->count == 0) {
1035             PrintErr(ERR_DUPLICATE_OPTION);
1036             return ERR_DUPLICATE_OPTION;
1037         }
1038         entry->count--;
1039         if (entry->cmd == ControlCmd::NOT_CMD) {
1040             int ret = entry->handler(context, optarg);
1041             if (ret != RET_SUCCESS) {
1042                 PrintErr(ret);
1043                 return ret;
1044             } else {
1045                 continue;
1046             }
1047         }
1048         cmdEntry = entry;
1049         cmdCount++;
1050         if (optarg != nullptr) {
1051             cmdArgs = optarg;
1052         }
1053     }
1054     if (cmdCount > 1) {
1055         cerr << ErrorCode2Str(ERR_COMMAND_INVALID) << endl;
1056         return ERR_COMMAND_INVALID;
1057     }
1058     // 2. Process CMD_XXX
1059     int ret = cmdEntry->handler(context, cmdArgs.c_str());
1060     if (ret != RET_SUCCESS) {
1061         PrintErr(ret);
1062         return ret;
1063     }
1064     return RET_SUCCESS;
1065 }
1066 } // namespace HiviewDFX
1067 } // namespace OHOS
1068 
main(int argc,char * argv[])1069 int main(int argc, char* argv[])
1070 {
1071     (void)OHOS::HiviewDFX::HilogEntry(argc, argv);
1072     return 0;
1073 }
1074