xref: /test/ostest/wukong/report/src/report.cpp (revision a69a01cd)
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 "report.h"
17
18#include <algorithm>
19#include <dirent.h>
20#include <fstream>
21#include <iostream>
22#include <sstream>
23#include <sys/inotify.h>
24#include <sys/stat.h>
25#include <unistd.h>
26
27#include "ability_manager_client.h"
28#include "element_name.h"
29#include "exception_manager.h"
30#include "filter_category.h"
31#include "format_csv.h"
32#include "format_json.h"
33#include "statistics_ability.h"
34#include "statistics_componment.h"
35#include "statistics_event.h"
36#include "statistics_exception.h"
37#include "string_ex.h"
38#include "wukong_define.h"
39#include "wukong_util.h"
40
41namespace OHOS {
42namespace WuKong {
43namespace {
44const uint32_t SEGMENT_STATISTICS_LENGTH = 10;
45std::string crashDir = "/data/log/faultlog/faultlogger/";
46void ListenCrashDir()
47{
48    int fd;
49    int wd;
50    ssize_t readLenght;
51    char buf[BUFSIZ];
52    char* bufPtr = nullptr;
53    struct inotify_event *event;
54    fd = inotify_init();
55    INFO_LOG("init notify");
56    if (fd < 0) {
57        return;
58    }
59    wd = inotify_add_watch(fd, "/data/log/faultlog/faultlogger/", IN_CLOSE_WRITE);
60    INFO_LOG("add_watch");
61    if (wd < 0) {
62        ERROR_LOG("inotify_add_watch /data/log/faultlog/faultlogger/ failed");
63        return;
64    }
65    buf[sizeof(buf) - 1] = 0;
66    std::string destDir = Report::GetInstance()->GetReportExceptionDir();
67    while ((readLenght = read(fd, buf, sizeof(buf) - 1)) > 0) {
68        uint32_t len = static_cast<uint32_t>(readLenght);
69        uint32_t nread = 0;
70        while (len > 0) {
71            bufPtr = &buf[nread];
72            void* middleType =  static_cast<void *>(bufPtr);
73            event = static_cast<struct inotify_event *>(middleType);
74            if ((event->mask & IN_CLOSE_WRITE) && (event->len > 0)) {
75                DEBUG_LOG_STR("event->mask{%x}", event->mask);
76                std::string targetFile(event->name);
77                WuKongUtil::GetInstance()->CopyFile(targetFile, crashDir, destDir);
78                DEBUG_LOG_STR("%s --- IN_CLOSE_WRITE\n", event->name);
79                Report::GetInstance()->ExceptionRecord(targetFile);
80            }
81            nread = nread + sizeof(struct inotify_event) + event->len;
82            len = len - sizeof(struct inotify_event) - event->len;
83        }
84    }
85    INFO_LOG("exit thread");
86    return;
87}
88
89void StartCrashDirListen()
90{
91    std::thread listenerThread(&ListenCrashDir);
92    INFO_LOG("create listener thread");
93    listenerThread.detach();
94    INFO_LOG("thread detach");
95}
96}  // namespace
97using namespace OHOS::AAFwk;
98Report::Report()
99{
100    EnvInit();
101    DataSetInit();
102}
103
104void Report::EnvInit()
105{
106    const std::string DEFAULT_DIR = "/data/local/tmp/wukong/report/";
107    startRunTime_ = WuKongUtil::GetInstance()->GetStartRunTime();
108    // Get a screenshot within the previous timestamp of the current timestamp
109    DIR *dirp = nullptr;
110    dirp = opendir(DEFAULT_DIR.c_str());
111    std::string maxValue = "";
112    std::string targetTimeDir;
113    // setting filename
114    currentTestDir_ = WuKongUtil::GetInstance()->GetCurrentTestDir();
115    INFO_LOG_STR("Report currentTestDir: (%s)", currentTestDir_.c_str());
116    // setting filename
117    reportCsvFileName_ = currentTestDir_ + "wukong_report.csv";
118    reportJsonFileName_ = currentTestDir_ + "data.js";
119    reportFocusInputFileName_ = currentTestDir_ + "focus_report";
120
121    INFO_LOG_STR("Report CSV: (%s)", reportCsvFileName_.c_str());
122    INFO_LOG_STR("Report JSON: (%s)", reportJsonFileName_.c_str());
123
124    reportExceptionDir_ = currentTestDir_ + "exception/";
125    INFO_LOG_STR("Report exception dir: (%s)", reportExceptionDir_.c_str());
126    int dirExist = access(reportExceptionDir_.c_str(), F_OK);
127    if (dirExist != 0) {
128        int dirStatus = mkdir((reportExceptionDir_).c_str(), 0777);
129        if (dirStatus == -1) {
130            ERROR_LOG("exception dir create fail");
131        }
132    }
133    StartCrashDirListen();
134    // register crash catcher
135    ExceptionManager::GetInstance()->StartCatching();
136    if (dirp == nullptr) {
137        ERROR_LOG_STR("dir{%s} opendir error", DEFAULT_DIR.c_str());
138        return;
139    }
140    while (dirp != nullptr) {
141        struct dirent *dp;
142        if ((dp = readdir(dirp)) == NULL) {
143            break;
144        }
145        std::string currentStringName(dp->d_name);
146        if (currentStringName != startRunTime_) {
147            if (currentStringName > maxValue) {
148                maxValue = currentStringName;
149                targetTimeDir = currentStringName;
150            }
151        }
152    }
153    (void)closedir(dirp);
154    // Delete the screenshot under the timestamp
155    std::string targetDir_ = DEFAULT_DIR + targetTimeDir +"/screenshot/";
156    WuKongUtil::GetInstance()->DeleteFile(targetDir_);
157}
158
159void Report::DataSetInit()
160{
161    std::shared_ptr<Filter> categoryFilter = std::make_shared<FilterCategory>();
162    eventDataSet_->SetFilterStragety(categoryFilter);
163    eventDataSet_->SetFilterType("event");
164    std::shared_ptr<Statistics> eventSatistics = std::make_shared<StatisticsEvent>();
165    eventDataSet_->SetStatisticsStragety(eventSatistics);
166
167    // set componment filter,statistics,format
168    componmentDataSet_->SetFilterStragety(categoryFilter);
169    componmentDataSet_->SetFilterType("componment");
170    std::shared_ptr<Statistics> componmentSatistics = std::make_shared<StatisticsComponment>();
171    componmentDataSet_->SetStatisticsStragety(componmentSatistics);
172
173    // set ability filter,statistics,format
174    abilityDataSet_->SetFilterStragety(categoryFilter);
175    abilityDataSet_->SetFilterType("abilityName");
176    std::shared_ptr<Statistics> abilitySatistics = std::make_shared<StatisticsAbility>();
177    abilityDataSet_->SetStatisticsStragety(abilitySatistics);
178
179    // set exception filter,statistics,format
180    exceptionDataSet_->SetFilterStragety(categoryFilter);
181    exceptionDataSet_->SetFilterType("exception");
182    std::shared_ptr<Statistics> exceptionSatistics = std::make_shared<StatisticsException>();
183    exceptionDataSet_->SetStatisticsStragety(exceptionSatistics);
184}
185
186void Report::SyncInputInfo(std::shared_ptr<InputedMsgObject> inputedMsgObject)
187{
188    TRACK_LOG_STD();
189    std::shared_ptr<AbilityManagerClient> abilityManagerClient = AbilityManagerClient::GetInstance();
190    OHOS::AppExecFwk::ElementName elementName = abilityManagerClient->GetTopAbility();
191    std::map<std::string, std::string> data;
192    data["bundleName"] = elementName.GetBundleName();
193    data["abilityName"] = elementName.GetAbilityName();
194    DEBUG_LOG_STR("bundleName{%s} abilityName{%s} ", data["bundleName"].c_str(), data["abilityName"].c_str());
195    SplitInputMode(inputedMsgObject, data);
196
197    // first appswitch abandon
198    std::map<std::string, std::string>::iterator it = data.find("event");
199    if (it != data.end() && (data["event"] == "appswitch") && (isFirstAppSwitch_ == false)) {
200        DEBUG_LOG("first appswitch abandon");
201        isFirstAppSwitch_ = true;
202        return;
203    }
204    // record app used to control data display
205    std::vector<std::string>::iterator bundleIter = std::find(bundles_.begin(), bundles_.end(), data["bundleName"]);
206    if (bundleIter == bundles_.end()) {
207        DEBUG_LOG_STR("push apps item{%s}", data["bundleName"].c_str());
208        bundles_.push_back(data["bundleName"]);
209    }
210    // send `k => v` to filter
211    eventDataSet_->FilterData(data);
212    componmentDataSet_->FilterData(data);
213    abilityDataSet_->FilterData(data);
214    taskCount_++;
215    DEBUG_LOG_STR("taskCount{%d}", taskCount_);
216    if (is_focus_) {
217        GroupFocusDataAndRecord(inputedMsgObject, data);
218    }
219    // statistics and storage every 10 data
220    if ((taskCount_ % SEGMENT_STATISTICS_LENGTH) == 0) {
221        HilogFileRecord();
222        SegmentedWriteCSV();
223        SegmentedWriteJson();
224        if (is_focus_) {
225            SegmentedWriteForFocusInput();
226        }
227    }
228    TRACK_LOG_END();
229}
230
231void Report::SplitInputMode(std::shared_ptr<InputedMsgObject> &inputedMsgObject,
232    std::map<std::string, std::string> &data)
233{
234    inputedMode inputMode = inputedMsgObject->GetInputedMode();
235    switch (inputMode) {
236        case multimodeInput: {
237            auto inputMutlMsgPtr = std::static_pointer_cast<MultimodeInputMsg>(inputedMsgObject);
238            data["event"] = inputMutlMsgPtr->GetInputType();
239            DEBUG_LOG_STR("eventType{%s}", data["event"].c_str());
240            break;
241        }
242
243        case componmentInput: {
244            auto inputCompMsgPtr = std::static_pointer_cast<ComponmentInputMsg>(inputedMsgObject);
245            ComponmentInfoArrange(data["bundleName"], inputCompMsgPtr, data);
246            DEBUG_LOG("componmentType map");
247            break;
248        }
249        default:
250            break;
251    }
252}
253
254void Report::GroupFocusDataAndRecord(std::shared_ptr<InputedMsgObject> &inputedMsgObject,
255    std::map<std::string, std::string> &data)
256{
257    TRACK_LOG_STD();
258    inputedMode inputMode = inputedMsgObject->GetInputedMode();
259    if (inputMode != componmentInput) {
260        return;
261    }
262    auto inputCompMsgPtr = std::static_pointer_cast<ComponmentInputMsg>(inputedMsgObject);
263    std::string item = "";
264    time_t currentTime = time(0);
265    std::string timeStr = "";
266    if (currentTime > 0) {
267        timeStr = std::to_string(currentTime);
268    }
269    item += std::to_string(taskCount_) + ",";
270    item += timeStr + ",";
271    item += data["abilityName"] + ",";
272    item += inputCompMsgPtr->pagePath_ + ",";
273    item += inputCompMsgPtr->componmentType_ + ",";
274    item += std::to_string(inputCompMsgPtr->startX_) + ",";
275    item += std::to_string(inputCompMsgPtr->startY_) + ",";
276    item += std::to_string(inputCompMsgPtr->endX_) + ",";
277    item += std::to_string(inputCompMsgPtr->endY_) + ",";
278    item += inputCompMsgPtr->content_ + ",";
279    item += std::to_string(inputCompMsgPtr->pssTotal_);
280    focus_input_vec_.push_back(item);
281    TRACK_LOG_END();
282}
283
284Report::~Report()
285{
286}
287
288void Report::SegmentedWriteCSV()
289{
290    TRACK_LOG_STD();
291    // csv report format
292    if (reportCsvFileName_.empty()) {
293        return;
294    }
295    std::shared_ptr<FormatCSV> formatCSV = std::make_shared<FormatCSV>();
296    eventDataSet_->SetFormatStragety(formatCSV);
297    componmentDataSet_->SetFormatStragety(formatCSV);
298    abilityDataSet_->SetFormatStragety(formatCSV);
299    exceptionDataSet_->SetFormatStragety(formatCSV);
300    std::stringstream modules;
301    modules << "module, Base Info" << std::endl;
302    modules << "name, base" << std::endl;
303    modules << "detail, info" << std::endl;
304    modules << "name, base, detail, info" << std::endl;
305    modules << "task status, success" << std::endl;
306    modules << "task time  , " << time(0) - startTime_ << std::endl;
307    if (!seed_.empty()) {
308        modules << "seed , " << seed_ << std::endl;
309    }
310    modules << "task count , " << taskCount_ << std::endl;
311    DEBUG_LOG("start event statistics");
312    eventDataSet_->StatisticsData();
313    DEBUG_LOG("end event statistics");
314    DEBUG_LOG("start componment statistics");
315    componmentDataSet_->StatisticsData();
316    DEBUG_LOG("end componment statistics");
317    std::string moduleInput;
318    modules << "module, Input Message Statistics" << std::endl;
319    modules << "name, all";
320    // show all app and detail
321    for (auto bundleIter : bundles_) {
322        modules << ", " << bundleIter;
323    }
324    modules << std::endl;
325    modules << "detail, event, componment" << std::endl;
326    eventDataSet_->FormatData("all", moduleInput);
327    componmentDataSet_->FormatData("all", moduleInput);
328    // loop app show name-type statistics content
329    for (auto bundleIter : bundles_) {
330        eventDataSet_->FormatData(bundleIter, moduleInput);
331        componmentDataSet_->FormatData(bundleIter, moduleInput);
332    }
333    modules << moduleInput;
334    modules << "module, ability Statistics" << std::endl;
335    modules << "name, all" << std::endl;
336    modules << "detail, ability" << std::endl;
337    moduleInput = "";
338    abilityDataSet_->StatisticsData();
339    abilityDataSet_->FormatData("all", moduleInput);
340    modules << moduleInput;
341
342    std::unique_lock<std::mutex> locker(crashMtx_);
343    modules << "module, Exception Message Statistics" << std::endl;
344    modules << "name, exception" << std::endl;
345    modules << "detail, statistics" << std::endl;
346    moduleInput = "";
347    exceptionDataSet_->StatisticsData();
348    exceptionDataSet_->FormatData("exception", moduleInput);
349    modules << moduleInput;
350    locker.unlock();
351    std::string csvContent = modules.str();
352    std::fstream csvFileStream(reportCsvFileName_, std::ios::out | std::ios::trunc);
353    csvFileStream << csvContent << std::endl;
354    csvFileStream.close();
355    TRACK_LOG_END();
356}
357
358void Report::SegmentedWriteJson()
359{
360    TRACK_LOG_STD();
361    DEBUG_LOG("SegmentedWriteJson start");
362    // csv report format
363    if (reportCsvFileName_.empty()) {
364        return;
365    }
366    std::shared_ptr<FormatJSON> formatJSON = std::make_shared<FormatJSON>();
367    eventDataSet_->SetFormatStragety(formatJSON);
368    componmentDataSet_->SetFormatStragety(formatJSON);
369    abilityDataSet_->SetFormatStragety(formatJSON);
370    exceptionDataSet_->SetFormatStragety(formatJSON);
371    std::stringstream modules;
372    std::string moduleInput;
373    modules << "var reportJson = {" << std::endl;
374    modules << "base: [" << std::endl;
375    modules << "{ item: \"task status\", detail: \" success \"}," << std::endl;
376    modules << "{ item: \"task time\", detail: \" " << time(0) - startTime_ << "s\"}," << std::endl;
377    modules << "{ item: \"task count\", detail: \" " << taskCount_ << "\"}," << std::endl;
378    if (!seed_.empty()) {
379        modules << "{ item: \"seed\", detail: \" " << seed_ << "\"}," << std::endl;
380    }
381    modules << "]," << std::endl;
382    modules << "detailApps:{" << std::endl;
383    modules << "names:[ \"all\"";
384    // show all app and detail
385    for (auto bundleIter : bundles_) {
386        modules << ", \"" << bundleIter << " \"";
387    }
388    modules << "]," << std::endl;
389    modules << "details: [" << std::endl;
390    modules << "{" << std::endl;
391    modules << "eventStatistics:" << std::endl;
392    eventDataSet_->FormatData("all", moduleInput);
393    modules << moduleInput;
394    modules << "controlStatistics:";
395    componmentDataSet_->FormatData("all", moduleInput);
396    modules << moduleInput;
397    modules << "},";
398    // loop app show name-type statistics content
399    for (auto bundleIter : bundles_) {
400        modules << "{" << std::endl;
401        modules << "eventStatistics:";
402        eventDataSet_->FormatData(bundleIter, moduleInput);
403        modules << moduleInput;
404        modules << "controlStatistics:";
405        componmentDataSet_->FormatData(bundleIter, moduleInput);
406        modules << moduleInput;
407        modules << "},";
408    }
409    modules << "]" << std::endl;
410    modules << "}," << std::endl;
411    modules << "abilityStatistics:";
412    abilityDataSet_->FormatData("all", moduleInput);
413    modules << moduleInput;
414    modules << "detailException: {" << std::endl;
415    modules << "names: [\"exception statistics\", \"cpp crash statistics\", \"js crash statistics\"]," << std::endl;
416    modules << "details: [" << std::endl;
417    modules << "{" << std::endl;
418    modules << "exception_statistics: {" << std::endl;
419    modules << "header: [\"Type\", \"Times\", \"Proportion\"]," << std::endl;
420    modules << "content: " << std::endl;
421    exceptionDataSet_->FormatData("exception", moduleInput);
422    modules << moduleInput;
423    modules << "}," << std::endl;
424    modules << "}," << std::endl;
425    modules << "]" << std::endl;
426    modules << "}," << std::endl;
427    unsigned int index = 0;
428    modules << "screens:[" << std::endl;
429    for (auto srceen : screenPaths_) {
430        modules << "{index:\"" << index << "\","
431                << "path:\"" << srceen << "\"}," << std::endl;
432        index++;
433    }
434    modules << "]," << std::endl;
435    modules << "};" << std::endl;
436    std::string jsonContent = modules.str();
437    std::fstream jsonFileStream(reportJsonFileName_, std::ios::out | std::ios::trunc);
438    jsonFileStream << jsonContent << std::endl;
439    jsonFileStream.close();
440    DEBUG_LOG("SegmentedWriteJson end");
441    TRACK_LOG_END();
442}
443
444void Report::SegmentedWriteForFocusInput()
445{
446    TRACK_LOG_STD();
447    DEBUG_LOG("SegmentedWriteForFocusInput start");
448    // csv report format
449    if (reportFocusInputFileName_.empty()) {
450        return;
451    }
452
453    std::stringstream modules;
454    for (size_t i = 0; i < focus_input_vec_.size(); ++i) {
455        modules << focus_input_vec_[i];
456        if (i < focus_input_vec_.size() - 1) {
457            modules << std::endl;
458        }
459    }
460    focus_input_vec_.clear();
461    std::string jsonContent = modules.str();
462    std::fstream jsonFileStream(reportFocusInputFileName_, std::ios::app);
463    jsonFileStream << jsonContent << std::endl;
464    jsonFileStream.close();
465    DEBUG_LOG("SegmentedWriteForFocusInput end");
466    TRACK_LOG_END();
467}
468
469void Report::HilogFileRecord()
470{
471    struct dirent *dp;
472    DIR *dirpHilog = nullptr;
473    std::shared_ptr<WuKongUtil> utilPtr = WuKongUtil::GetInstance();
474    dirpHilog = opendir(hilogDirs_.c_str());
475    if (dirpHilog == nullptr) {
476        ERROR_LOG_STR("dir{%s} opendir error", hilogDirs_.c_str());
477        return;
478    }
479    while ((dp = readdir(dirpHilog)) != NULL) {
480        std::string targetFile(dp->d_name);
481        if ((strcmp(dp->d_name, ".") != 0) && (strcmp(dp->d_name, "..") != 0)) {
482            std::vector<std::string>::iterator iterDir = find(hilogFiles_.begin(), hilogFiles_.end(), targetFile);
483            if (iterDir == hilogFiles_.end()) {
484                DEBUG_LOG("hilog copy action");
485                utilPtr->CopyFile(targetFile, hilogDirs_, reportExceptionDir_);
486                hilogFiles_.push_back(targetFile);
487            }
488        }
489    }
490    if (dirpHilog != nullptr) {
491        (void)closedir(dirpHilog);
492    }
493}
494
495void Report::ExceptionRecord(const std::string &exceptionFilename)
496{
497    std::unique_lock<std::mutex> locker(crashMtx_);
498    std::map<std::string, std::string> data;
499    std::string exceptionType;
500    if (exceptionFilename.find("cppcrash") != std::string::npos) {
501        exceptionType = "cppcrash";
502    }
503
504    if (exceptionFilename.find("appfreeze") != std::string::npos) {
505        exceptionType = "appfreeze";
506    }
507
508    if (exceptionFilename.find("jscrash") != std::string::npos) {
509        exceptionType = "jscrash";
510    }
511
512    if (exceptionFilename.find("serviceblock") != std::string::npos) {
513        exceptionType = "serviceblock";
514    }
515
516    data["exception"] = exceptionType;
517    exceptionDataSet_->FilterData(data);
518}
519
520void Report::Finish()
521{
522    SegmentedWriteCSV();
523    SegmentedWriteJson();
524    if (is_focus_) {
525        SegmentedWriteForFocusInput();
526    }
527    ExceptionManager::GetInstance()->StopCatching();
528}
529
530void Report::SetSeed(std::string seed)
531{
532    seed_ = seed;
533}
534
535void Report::ComponmentInfoArrange(const std::string &bundle, std::shared_ptr<ComponmentInputMsg> inputCompMsgPtr,
536                                   std::map<std::string, std::string> &data)
537{
538    std::map<std::string, componmentRecord>::iterator bundleComponmentRecordIter;
539    componmentRecord componmentRecord;
540    bundleComponmentRecordIter = bundleComponmentRecord_.find(bundle);
541    if (bundleComponmentRecordIter != bundleComponmentRecord_.end()) {
542        componmentRecord = bundleComponmentRecordIter->second;
543    }
544    componmentRecord.pageIdComponments[inputCompMsgPtr->pageId_] = inputCompMsgPtr->pageComponments;
545    std::map<std::string, uint32_t>::iterator componmentTypeCountIter;
546    uint32_t componmentTypeInputedCount = 0;
547    uint32_t componmentTypeTotal = 0;
548    componmentTypeCountIter = componmentRecord.componmentTypeCount.find(inputCompMsgPtr->componmentType_);
549    if (componmentTypeCountIter != componmentRecord.componmentTypeCount.end()) {
550        componmentTypeInputedCount = componmentTypeCountIter->second;
551    }
552    componmentTypeInputedCount++;
553
554    for (auto pageIdComponmentsIter : componmentRecord.pageIdComponments) {
555        for (auto componmentVectorIter : pageIdComponmentsIter.second) {
556            if (componmentVectorIter.compare(inputCompMsgPtr->componmentType_) == 0) {
557                componmentTypeTotal++;
558            }
559        }
560    }
561    if (componmentTypeInputedCount > componmentTypeTotal) {
562        componmentTypeInputedCount = componmentTypeTotal;
563    }
564
565    componmentRecord.componmentTypeCount[inputCompMsgPtr->componmentType_] = componmentTypeInputedCount;
566    data["componment"] = inputCompMsgPtr->componmentType_;
567    data["inputedTimes"] = std::to_string(componmentTypeInputedCount);
568    data["componmentTotals"] = std::to_string(componmentTypeTotal);
569    DEBUG_LOG_STR("componmentType{%s} inputedTimes{%s} componmentTotals{%s}", data["componment"].c_str(),
570                  data["inputedTimes"].c_str(), data["componmentTotals"].c_str());
571    bundleComponmentRecord_[bundle] = componmentRecord;
572}
573
574void Report::RecordScreenPath(const std::string &screenPath)
575{
576    TRACK_LOG_STD();
577    screenPaths_.push_back(screenPath);
578    TRACK_LOG_END();
579}
580
581std::string Report::GetReportExceptionDir()
582{
583    return reportExceptionDir_;
584}
585}  // namespace WuKong
586}  // namespace OHOS