DeveloperPage.qml 10.4 KB
Newer Older
Harald Sitter's avatar
Harald Sitter committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
// SPDX-FileCopyrightText: 2021 Harald Sitter <sitter@kde.org>

import QtQuick 2.15
import QtQuick.Layouts 1.15
import QtQuick.Controls 2.15 as QQC2
import org.kde.kirigami 2.19 as Kirigami
import org.kde.syntaxhighlighting 1.0

import org.kde.drkonqi 1.0

Kirigami.ScrollablePage {
    id: page

    property string trace: ""
    property bool basic: false
    property alias usefulness: ratingItem.usefulness
    property alias footerActionsLeft: footerBarLeft.actions
    property alias footerActionsRight: footerBarRight.actions

21
22
23
    padding: 0
    bottomPadding: 0

Harald Sitter's avatar
Harald Sitter committed
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
    title: i18nc("@title:window", "Developer Information")
    onStateChanged: {
        console.log("state")
        console.log(state)
    }

    mainAction: Kirigami.Action {
        id: installButton
        visible: false
        text: i18nc("@action:button", "Install Debug Symbols")
        onTriggered: debugPackageInstaller.installDebugPackages()
        iconName: "install"
        tooltip: i18nc("@info:tooltip", "Use this button to install the missing debug symbols packages.")
    }

    contextualActions: [
        Kirigami.Action {
            id: reloadAction
            enabled: BacktraceGenerator.state !== BacktraceGenerator.Loading
            iconName: "view-refresh"
            text: i18nc("@action:button", "Reload")
            visible: !CrashedApplication.hasBeenRestarted
            tooltip: xi18nc("@info:tooltip",
`Use this button to reload the crash information (backtrace). This is useful when you have
installed the proper debug symbol packages and you want to obtain a better backtrace.`)
            onTriggered: BacktraceGenerator.start()
        },

        Kirigami.Action {
            displayHint: Kirigami.Action.DisplayHint.AlwaysHide
            iconName: "edit-copy"
            text: i18nc("@action:button", "Copy")
            tooltip: i18nc("@info:tooltip", "Use this button to copy the crash information (backtrace) to the clipboard.")
            onTriggered: DrKonqi.copyToClipboard(traceArea.text)
        },

        Kirigami.Action {
            displayHint: Kirigami.Action.DisplayHint.AlwaysHide
            iconName: "document-save"
            text: i18nc("@action:button", "Save")
            tooltip: xi18nc("@info:tooltip",
`Use this button to save the crash information (backtrace) to a file. This is useful if you want to take a look at it or to report the bug later.`)
            onTriggered: DrKonqi.saveReport(traceArea.text)
        }
    ]

    header: QQC2.ToolBar {
        RatingItem {
            id: ratingItem
            anchors.fill: parent
            failed: BacktraceGenerator.state === BacktraceGenerator.Failed || BacktraceGenerator.state === BacktraceGenerator.FailedToStart
Harald Sitter's avatar
Harald Sitter committed
75
            loading: BacktraceGenerator.state === BacktraceGenerator.Loading
Harald Sitter's avatar
Harald Sitter committed
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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
        }
    }

    ColumnLayout {
        DebugPackageInstaller { // not in global scope because it messes up scrollbars
            id: debugPackageInstaller
        }

        RowLayout {
            visible: page.basic
            Layout.fillHeight: true
            Kirigami.Icon {
                source: "help-hint"
                width: Kirigami.Units.iconSizes.enormous
                height: width
            }
            QQC2.Label {
                Layout.fillHeight: true
                Layout.fillWidth: true
                wrapMode: Text.Wrap
                text: xi18nc("@info",
`<subtitle>What is a "backtrace" ?</subtitle><para>A backtrace basically describes what was
happening inside the application when it crashed, so the developers may track
down where the mess started. They may look meaningless to you, but they might
actually contain a wealth of useful information.<nl />Backtraces are commonly
used during interactive and post-mortem debugging.</para>`)
            }
        }
        QQC2.TextArea {
            id: traceArea
            Layout.fillWidth: true
            Layout.fillHeight: true
            visible: text !== "" && !page.basic

            // text: output.text
            font.family: "monospace"
            wrapMode: Text.Wrap
            textFormat: TextEdit.PlainText
            readOnly: true
            selectByMouse: Kirigami.Settings.isMobile ? false : true

            SyntaxHighlighter {
                textEdit:  BacktraceGenerator.debuggerIsGDB() ? traceArea : undefined
                definition: "GDB Backtrace"
            }

            Connections {
                id: generatorConnections
                target: BacktraceGenerator
                function onNewLine(line) { traceArea.text += line }
                function onStateChanged() {
                    console.log(BacktraceGenerator.state)
                    console.log(BacktraceGenerator.Loaded)

                    const state = BacktraceGenerator.state
                    page.state = state

                    installButton.visible = false

                    const parser = BacktraceGenerator.parser();
                    usefulness = parser.backtraceUsefulness()
                    // ratingItem.usefulness = usefulness
                    if (state == BacktraceGenerator.Loaded) {
                        traceArea.text = BacktraceGenerator.backtrace()
                        trace = traceArea.text // FIXME ensure this doesn't result in a binding

                        if (usefulness != BacktraceParser.ReallyUseful) {
                            if (debugPackageInstaller.canInstallDebugPackages) {
                                detailsLabel.text = xi18nc("@info/rich",
`You can click the <interface>Install Debug Symbols</interface> button in order to automatically install the missing debugging information packages. If this method
does not work: please read <link url='%1'>How to create useful crash reports</link> to learn how to get a useful
backtrace; install the needed packages (<link url='%2'> list of files</link>) and click the <interface>Reload</interface> button.`,
                                                            Globals.techbaseHowtoDoc, '#missingDebugPackages')
                                installButton.visible = true
                                debugPackageInstaller.setMissingLibraries(parser.librariesWithMissingDebugSymbols())
                            } else {
                                detailsLabel.text = xi18nc("@info/rich",
`Please read <link url='%1'>How to create useful crash reports</link> to learn how to get a useful backtrace; install the needed packages (<link url='%2'>
list of files</link>) and click the <interface>Reload</interface> button.`,
                                                            Globals.techbaseHowtoDoc, '#missingDebugPackages')
                            }
                        }
                    } else if (state == BacktraceGenerator.Failed) {
                        traceArea.text = i18nc("@info:status", "The crash information could not be generated.")
                        detailsLabel.text = xi18nc("@info/rich", `You could try to regenerate the backtrace by clicking the <interface>Reload</interface> button.`)
                    } else if (state == BacktraceGenerator.FailedToStart) {
                        // FIXME dupe from failed
                        traceArea.text = i18nc("@info:status", "The crash information could not be generated.")
                        detailsLabel.text = xi18nc("@info/rich",
`<emphasis strong='true'>You need to first install the debugger application (%1) then click the <interface>Reload</interface> button.</emphasis>`,
                                                   BacktraceGenerator.debuggerName())
                    }
                }
            }
        }
    }

    footer: ColumnLayout {
        QQC2.Control { // Get standard padding so it doesn't stick to the edges, Label has none by default cause it isn't a Control
            Layout.fillWidth: true
            background: null

            Kirigami.PromptDialog {
                id: filesDialog
                title: i18nc("@title", "Not Sufficiently Useful")
                function reload() {
                    const parser = BacktraceGenerator.parser()
                    let missingDbgForFiles = parser.librariesWithMissingDebugSymbols()
                    missingDbgForFiles.unshift(CrashedApplication.exectuableAbsoluteFilePath)
                    // TODO should maybe prepend DrKonqi::crashedApplication()->executable().absoluteFilePath()
                    // NB: cannot use xi18nc here because that'd close the html tag but we need to append an unordered list of paths
                    let message = "<html>" + i18n("The packages containing debug information for the following application and libraries are missing:") + "<br /><ul>";
                    for (const i in missingDbgForFiles) {
                        message += "<li>" + missingDbgForFiles[i] + "</li>";
                    }
                    message += "</ul></html>"
                    subtitle = message
                }

                showCloseButton: true
            }

            contentItem: QQC2.Label {
                id: detailsLabel
                wrapMode: Text.Wrap
                onLinkActivated: {
                    if (link[0] == "#") { // in-page reference
                        filesDialog.reload()
                        filesDialog.open()
                    } else {
                        Qt.openUrlExternally(link)
                    }
                }
                textFormat: Text.RichText
            }
        }
        RowLayout {
            // Two bars because of https://bugs.kde.org/show_bug.cgi?id=451026
            // Awkard though, maybe we should just live with everything being right aligned
            FooterActionBar {
                padding: 0
                id: footerBarLeft
                alignment: Qt.AlignLeft
                visible: actions.length > 0
            }
            FooterActionBar {
                padding: 0
                id: footerBarRight
                visible: actions.length > 0
            }
        }
    }

    Component.onCompleted: {
        if (BacktraceGenerator.state === BacktraceGenerator.NotLoaded) {
            reloadAction.trigger()
        } else {
            // ensure our current state is the state of the backend. this is important in case the developer page
            // was used before the report workflow (i.e. the data was generated but from a different view)
            generatorConnections.onStateChanged()
        }
    }
}