aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/qnx/slog2inforunner.cpp
blob: ad2348dd80d98180fe25ff62c853b5710169463f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
// Copyright (C) 2016 BlackBerry Limited. All rights reserved.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0

#include "slog2inforunner.h"

#include "qnxtr.h"

#include <projectexplorer/devicesupport/idevice.h>
#include <projectexplorer/runconfigurationaspects.h>
#include <projectexplorer/runcontrol.h>

#include <QtTaskTree/QSingleTaskTreeRunner>

#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>

#include <QDateTime>
#include <QRegularExpression>

using namespace ProjectExplorer;
using namespace QtTaskTree;
using namespace Utils;

namespace Qnx::Internal {

struct SlogData
{
    RunControl *m_runControl = nullptr;
    QString m_applicationId;
    QDateTime m_launchDateTime = {};
    bool m_currentLogs = false;
    QString m_remainingData = {};

    void processLogLine(const QString &line);
    void processRemainingLogData();
    void processLogInput(const QString &input);
};

Group slog2InfoRecipe(RunControl *runControl)
{
    QString applicationId = runControl->aspectData<ExecutableAspect>()->executable.fileName();
    // See QTCREATORBUG-10712 for details.
    // We need to limit length of ApplicationId to 63 otherwise it would not match one in slog2info.
    applicationId.truncate(63);

    const Storage<SlogData> storage(SlogData{runControl, applicationId});

    const auto onTestSetup = [runControl](Process &process) {
        process.setCommand(CommandLine{runControl->device()->filePath("slog2info")});
    };
    const auto onTestDone = [runControl] {
        runControl->postMessage(Tr::tr("Warning: \"slog2info\" is not found on the device, "
                                       "debug output not available."), ErrorMessageFormat);
    };

    const auto onLaunchTimeSetup = [runControl](Process &process) {
        process.setCommand({runControl->device()->filePath("date"), "+\"%d %H:%M:%S\"", CommandLine::Raw});
    };
    const auto onLaunchTimeDone = [applicationId, storage](const Process &process) {
        QTC_CHECK(!applicationId.isEmpty());
        storage->m_launchDateTime = QDateTime::fromString(process.cleanedStdOut().trimmed(),
                                                          "dd HH:mm:ss");
    };

    const auto onLogSetup = [storage, runControl](Process &process) {
        process.setCommand({runControl->device()->filePath("slog2info"), {"-w"}});
        SlogData *slogData = storage.activeStorage();
        QObject::connect(&process, &Process::readyReadStandardOutput, &process,
                         [slogData, processPtr = &process] {
            slogData->processLogInput(QString::fromLatin1(processPtr->readAllRawStandardOutput()));
        });
        QObject::connect(&process, &Process::readyReadStandardError, &process,
                         [runControl, processPtr = &process] {
            runControl->postMessage(QString::fromLatin1(processPtr->readAllRawStandardError()), StdErrFormat);
        });
    };
    const auto onLogError = [runControl](const Process &process) {
        runControl->postMessage(Tr::tr("Cannot show slog2info output. Error: %1")
                                    .arg(process.errorString()), StdErrFormat);
    };

    const auto onCanceled = [storage] { storage->processRemainingLogData(); };

    return Group {
        storage,
        ProcessTask(onTestSetup, onTestDone, CallDone::OnError),
        ProcessTask(onLaunchTimeSetup, onLaunchTimeDone, CallDone::OnSuccess),
        ProcessTask(onLogSetup, onLogError, CallDone::OnError),
        onGroupDone(onCanceled, CallDone::OnCancel)
    }.withCancel(runControl->canceler());
}

void SlogData::processRemainingLogData()
{
    if (!m_remainingData.isEmpty())
        processLogLine(m_remainingData);
    m_remainingData.clear();
}

void SlogData::processLogInput(const QString &input)
{
    QStringList lines = input.split(QLatin1Char('\n'));
    if (lines.isEmpty())
        return;
    lines.first().prepend(m_remainingData);
    m_remainingData = lines.takeLast();
    for (const QString &line : std::as_const(lines))
        processLogLine(line);
}

void SlogData::processLogLine(const QString &line)
{
    // The "(\\s+\\S+)?" represents a named buffer. If message has noname (aka empty) buffer
    // then the message might get cut for the first number in the message.
    // The "\\s+(\\b.*)?$" represents a space followed by a message. We are unable to determinate
    // how many spaces represent separators and how many are a part of the messages, so resulting
    // messages has all whitespaces at the beginning of the message trimmed.
    static const QRegularExpression regexp(
        "^[a-zA-Z]+\\s+([0-9]+ [0-9]+:[0-9]+:[0-9]+.[0-9]+)\\s+(\\S+)(\\s+(\\S+))?\\s+([0-9]+)\\s+(.*)?$");

    const QRegularExpressionMatch match = regexp.match(line);
    if (!match.hasMatch())
        return;

    // Note: This is useless if/once slog2info -b displays only logs from recent launches
    if (!m_launchDateTime.isNull()) {
        // Check if logs are from the recent launch
        if (!m_currentLogs) {
            const QDateTime dateTime = QDateTime::fromString(match.captured(1),
                                                             QLatin1String("dd HH:mm:ss.zzz"));
            m_currentLogs = dateTime >= m_launchDateTime;
            if (!m_currentLogs)
                return;
        }
    }

    const QString applicationId = match.captured(2);
    if (!applicationId.startsWith(m_applicationId))
        return;

    const QString bufferName = match.captured(4);
    int bufferId = match.captured(5).toInt();
    // filtering out standard BB10 messages
    if (bufferName == QLatin1String("default") && bufferId == 8900)
        return;

    m_runControl->postMessage(match.captured(6).trimmed() + '\n', StdOutFormat);
}

} // Qnx::Internal