aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/qnx/slog2inforunner.cpp
blob: 21de26e5a246deee44e542501314b1e571258dc2 (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
151
// 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 <utils/qtcassert.h>
#include <utils/qtcprocess.h>

#include <QRegularExpression>

using namespace ProjectExplorer;
using namespace Utils;

namespace Qnx::Internal {

Slog2InfoRunner::Slog2InfoRunner(RunControl *runControl)
    : RunWorker(runControl)
{
    setId("Slog2InfoRunner");
    m_applicationId = runControl->aspect<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.
    m_applicationId.truncate(63);
}

void Slog2InfoRunner::start()
{
    using namespace Utils::Tasking;
    QTC_CHECK(!m_taskTree);

    const auto testStartHandler = [this](Process &process) {
        process.setCommand({device()->filePath("slog2info"), {}});
    };
    const auto testDoneHandler = [this](const Process &) {
        m_found = true;
    };
    const auto testErrorHandler = [this](const Process &) {
        appendMessage(Tr::tr("Warning: \"slog2info\" is not found on the device, "
                             "debug output not available."), ErrorMessageFormat);
    };

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

    const auto logStartHandler = [this](Process &process) {
        process.setCommand({device()->filePath("slog2info"), {"-w"}});
        connect(&process, &Process::readyReadStandardOutput, this, [&] {
            processLogInput(QString::fromLatin1(process.readAllRawStandardOutput()));
        });
        connect(&process, &Process::readyReadStandardError, this, [&] {
            appendMessage(QString::fromLatin1(process.readAllRawStandardError()), StdErrFormat);
        });
    };
    const auto logErrorHandler = [this](const Process &process) {
        appendMessage(Tr::tr("Cannot show slog2info output. Error: %1").arg(process.errorString()),
                      StdErrFormat);
    };

    const Tasking::Group root {
        ProcessTask(testStartHandler, testDoneHandler, testErrorHandler),
        ProcessTask(launchTimeStartHandler, launchTimeDoneHandler),
        ProcessTask(logStartHandler, {}, logErrorHandler)
    };

    m_taskTree.reset(new TaskTree(root));
    m_taskTree->start();
    reportStarted();
}

void Slog2InfoRunner::stop()
{
    m_taskTree.reset();
    processRemainingLogData();
    reportStopped();
}

bool Slog2InfoRunner::commandFound() const
{
    return m_found;
}

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

void Slog2InfoRunner::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 Slog2InfoRunner::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 QRegularExpression regexp(QLatin1String(
        "^[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) {
            QDateTime dateTime = QDateTime::fromString(match.captured(1),
                                                       QLatin1String("dd HH:mm:ss.zzz"));
            m_currentLogs = dateTime >= m_launchDateTime;
            if (!m_currentLogs)
                return;
        }
    }

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

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

    appendMessage(match.captured(6).trimmed() + '\n', StdOutFormat);
}

} // Qnx::Internal