Merge ~ubuntu-core-dev/ubuntu/+source/apport:ubuntu/devel-core-dump-handler into ~ubuntu-core-dev/ubuntu/+source/apport:ubuntu/devel
- Git
- lp:~ubuntu-core-dev/ubuntu/+source/apport
- ubuntu/devel-core-dump-handler
- Merge into ubuntu/devel
Proposed by
Benjamin Drung
Status: | Merged | ||||
---|---|---|---|---|---|
Merged at revision: | daeea20711e0bbd2f4341b3be5e002609f780766 | ||||
Proposed branch: | ~ubuntu-core-dev/ubuntu/+source/apport:ubuntu/devel-core-dump-handler | ||||
Merge into: | ~ubuntu-core-dev/ubuntu/+source/apport:ubuntu/devel | ||||
Diff against target: |
449 lines (+318/-22) 10 files modified
debian/apport-core-dump-handler.install (+2/-0) debian/apport.install (+4/-2) debian/changelog (+15/-0) debian/control (+18/-3) debian/patches/apport-retrace-split-out-report-loading-into-its-own-func.patch (+132/-0) debian/patches/fix-use-context-manager-when-manipulating-GzipFiles-LP-20.patch (+109/-0) debian/patches/retrace-directly-test-crashid-relevance.patch (+34/-0) debian/patches/series (+3/-0) debian/rules (+1/-16) dev/null (+0/-1) |
||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Simon Chopin | Needs Fixing | ||
git-ubuntu import | Pending | ||
Review via email: mp+458513@code.launchpad.net |
This proposal supersedes a proposal from 2024-01-12.
Commit message
Description of the change
To post a comment you must log in.
Revision history for this message
Benjamin Drung (bdrung) : Posted in a previous version of this proposal | # |
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/debian/apport-core-dump-handler.install b/debian/apport-core-dump-handler.install |
2 | new file mode 100644 |
3 | index 0000000..124c9fd |
4 | --- /dev/null |
5 | +++ b/debian/apport-core-dump-handler.install |
6 | @@ -0,0 +1,2 @@ |
7 | +debian/tmp/etc/init.d/apport |
8 | +usr/lib/systemd/system/apport.service |
9 | diff --git a/debian/apport.init b/debian/apport.init |
10 | deleted file mode 120000 |
11 | index 4c697d9..0000000 |
12 | --- a/debian/apport.init |
13 | +++ /dev/null |
14 | @@ -1 +0,0 @@ |
15 | -../etc/init.d/apport |
16 | \ No newline at end of file |
17 | diff --git a/debian/apport.install b/debian/apport.install |
18 | index da993f8..00e96b0 100644 |
19 | --- a/debian/apport.install |
20 | +++ b/debian/apport.install |
21 | @@ -4,8 +4,9 @@ debian/apport-autoreport.path /lib/systemd/system |
22 | debian/apport-autoreport.service /lib/systemd/system |
23 | debian/apport-autoreport.timer /lib/systemd/system |
24 | debian/package-hooks usr/share/apport |
25 | -debian/tmp/etc |
26 | -lib/systemd/ |
27 | +debian/tmp/etc/apport |
28 | +debian/tmp/etc/cron.daily |
29 | +debian/tmp/etc/default |
30 | lib/udev/ |
31 | usr/bin/apport-bug |
32 | usr/bin/apport-cli |
33 | @@ -13,6 +14,7 @@ usr/bin/apport-collect |
34 | usr/bin/apport-unpack |
35 | usr/bin/oem-getlogs |
36 | usr/lib/pm-utils |
37 | +usr/lib/systemd/system/apport-forward* |
38 | usr/lib/tmpfiles.d/ |
39 | usr/share/apport/apport |
40 | usr/share/apport/apport-checkreports |
41 | diff --git a/debian/changelog b/debian/changelog |
42 | index 8c290b6..e4e5854 100644 |
43 | --- a/debian/changelog |
44 | +++ b/debian/changelog |
45 | @@ -1,3 +1,18 @@ |
46 | +apport (2.27.0-0ubuntu7) noble; urgency=medium |
47 | + |
48 | + [ Benjamin Drung ] |
49 | + * Rely on pybuild in dh_auto_* targets |
50 | + * Introduce the separate apport-core-dump-handler package that registers as |
51 | + kernel crash dump handler. This is needed for the upcoming systemd-coredump |
52 | + support. |
53 | + * Move systemd units from /lib to /usr/lib |
54 | + |
55 | + [ Simon Chopin ] |
56 | + * Rework apport-retrace to handle unbound crashid (LP: #2051512) |
57 | + * fix: use context manager when manipulating GzipFiles (LP: #2051512) |
58 | + |
59 | + -- Benjamin Drung <bdrung@ubuntu.com> Wed, 14 Feb 2024 16:51:44 +0100 |
60 | + |
61 | apport (2.27.0-0ubuntu6) noble; urgency=medium |
62 | |
63 | * Move additional package hooks to debian/package-hooks |
64 | diff --git a/debian/control b/debian/control |
65 | index 630358f..d4c43fc 100644 |
66 | --- a/debian/control |
67 | +++ b/debian/control |
68 | @@ -39,6 +39,7 @@ Homepage: https://wiki.ubuntu.com/Apport |
69 | Package: apport |
70 | Architecture: all |
71 | Depends: |
72 | + apport-core-dump-handler, |
73 | gir1.2-glib-2.0 (>= 1.29.17), |
74 | python3, |
75 | python3-apport (>= ${source:Version}), |
76 | @@ -47,10 +48,8 @@ Depends: |
77 | ${misc:Depends}, |
78 | Recommends: apport-symptoms, python3-systemd |
79 | Suggests: apport-gtk | apport-kde, pkexec, polkitd |
80 | -Replaces: core-dump-handler, python-apport (<< 2.2-0ubuntu1) |
81 | +Replaces: python-apport (<< 2.2-0ubuntu1) |
82 | Breaks: python-apport (<< 2.2-0ubuntu1) |
83 | -Conflicts: core-dump-handler |
84 | -Provides: core-dump-handler |
85 | Pre-Depends: ${misc:Pre-Depends} |
86 | Description: automatically generate crash reports for debugging |
87 | apport automatically collects data from crashed processes and |
88 | @@ -96,6 +95,22 @@ Description: Python 3 library for Apport crash report handling |
89 | * Various frontend utility functions. |
90 | * Python hook to generate crash reports when Python scripts fail. |
91 | |
92 | +Package: apport-core-dump-handler |
93 | +Architecture: all |
94 | +Depends: apport (>= 2.27.0-0ubuntu7~), ${misc:Depends} |
95 | +Breaks: apport (<< 2.27.0-0ubuntu7~) |
96 | +Replaces: apport (<< 2.27.0-0ubuntu7~), core-dump-handler |
97 | +Conflicts: core-dump-handler |
98 | +Provides: core-dump-handler |
99 | +Pre-Depends: ${misc:Pre-Depends} |
100 | +Description: Kernel core dump handler for Apport |
101 | + apport automatically collects data from crashed processes and |
102 | + compiles a problem report in /var/crash/. This utilizes the crashdump |
103 | + helper hook provided by the Ubuntu kernel. |
104 | + . |
105 | + This package provides the service that configures the kernel (via |
106 | + /proc/sys/kernel/core_pattern) to forward the crash dumps to apport. |
107 | + |
108 | Package: apport-retrace |
109 | Section: devel |
110 | Architecture: all |
111 | diff --git a/debian/patches/apport-retrace-split-out-report-loading-into-its-own-func.patch b/debian/patches/apport-retrace-split-out-report-loading-into-its-own-func.patch |
112 | new file mode 100644 |
113 | index 0000000..c9e8529 |
114 | --- /dev/null |
115 | +++ b/debian/patches/apport-retrace-split-out-report-loading-into-its-own-func.patch |
116 | @@ -0,0 +1,132 @@ |
117 | +From: Simon Chopin <simon.chopin@canonical.com> |
118 | +Date: Tue, 13 Feb 2024 19:23:14 +0100 |
119 | +Subject: apport-retrace: split out report loading into its own function |
120 | + |
121 | +This reduces complexity in main() while also addressing the unbound crashid |
122 | +issue that showed up in Noble, albeit only partially: now it's not |
123 | +unbound, but it can be None, which is another can of worms. |
124 | + |
125 | +Baby steps! |
126 | + |
127 | +Origin: upstream, https://github.com/canonical/apport/pull/295 |
128 | +--- |
129 | + bin/apport-retrace | 55 ++++++++++++++++++++++++++++++++---------------------- |
130 | + 1 file changed, 33 insertions(+), 22 deletions(-) |
131 | + |
132 | +diff --git a/bin/apport-retrace b/bin/apport-retrace |
133 | +index f8082c6..246a065 100755 |
134 | +--- a/bin/apport-retrace |
135 | ++++ b/bin/apport-retrace |
136 | +@@ -26,12 +26,14 @@ import tempfile |
137 | + import termios |
138 | + import tty |
139 | + import zlib |
140 | ++from argparse import Namespace |
141 | + from gettext import gettext as _ |
142 | + |
143 | + import apport |
144 | + import apport.fileutils |
145 | + import apport.sandboxutils |
146 | +-from apport.crashdb import get_crashdb |
147 | ++from apport import Report |
148 | ++from apport.crashdb import CrashDatabase, get_crashdb |
149 | + |
150 | + |
151 | + def parse_args(): |
152 | +@@ -323,20 +325,10 @@ def print_traces(report): |
153 | + print(report["StacktraceSource"]) |
154 | + |
155 | + |
156 | +-# pylint: disable-next=missing-function-docstring |
157 | +-def main(): |
158 | +- # TODO: Split into smaller functions/methods |
159 | +- # pylint: disable=too-many-branches,too-many-locals,too-many-nested-blocks |
160 | +- # pylint: disable=too-many-statements |
161 | +- apport.memdbg("start") |
162 | +- |
163 | +- gettext.textdomain("apport") |
164 | +- |
165 | +- options = parse_args() |
166 | +- |
167 | +- crashdb = get_crashdb(options.auth) |
168 | +- apport.memdbg("got crash DB") |
169 | +- |
170 | ++def load_report( |
171 | ++ options: Namespace, crashdb: CrashDatabase |
172 | ++) -> tuple[Report, int | None]: |
173 | ++ """Load the initial report for the given CLI options""" |
174 | + # load the report |
175 | + if os.path.exists(options.report): |
176 | + try: |
177 | +@@ -344,13 +336,17 @@ def main(): |
178 | + with open(options.report, "rb") as f: |
179 | + report.load(f, binary="compressed") |
180 | + apport.memdbg("loaded report from file") |
181 | ++ return report, None |
182 | + except (MemoryError, TypeError, ValueError, OSError, zlib.error) as error: |
183 | + apport.fatal("Cannot open report file: %s", str(error)) |
184 | + elif options.report.isdigit(): |
185 | + # crash ID |
186 | + try: |
187 | +- report = crashdb.download(int(options.report)) |
188 | ++ crashid = int(options.report) |
189 | ++ report = crashdb.download(crashid) |
190 | + apport.memdbg("downloaded report from crash DB") |
191 | ++ options.report = None |
192 | ++ return report, crashid |
193 | + except AssertionError as error: |
194 | + if "apport format data" in str(error): |
195 | + apport.error("Broken report: %s", str(error)) |
196 | +@@ -389,14 +385,29 @@ Thank you for your understanding, and sorry for the inconvenience! |
197 | + sys.exit(0) |
198 | + else: |
199 | + raise |
200 | +- |
201 | +- crashid = options.report |
202 | +- options.report = None |
203 | + else: |
204 | + apport.fatal( |
205 | + '"%s" is neither an existing report file nor a crash ID', options.report |
206 | + ) |
207 | + |
208 | ++ |
209 | ++# pylint: disable-next=missing-function-docstring |
210 | ++def main(): |
211 | ++ # TODO: Split into smaller functions/methods |
212 | ++ # pylint: disable=too-many-branches,too-many-locals,too-many-nested-blocks |
213 | ++ # pylint: disable=too-many-statements |
214 | ++ apport.memdbg("start") |
215 | ++ |
216 | ++ gettext.textdomain("apport") |
217 | ++ |
218 | ++ options = parse_args() |
219 | ++ |
220 | ++ crashdb = get_crashdb(options.auth) |
221 | ++ apport.memdbg("got crash DB") |
222 | ++ |
223 | ++ # load the report |
224 | ++ report, crashid = load_report(options, crashdb) |
225 | ++ |
226 | + if options.core_file: |
227 | + report["CoreDump"] = (os.path.abspath(options.core_file),) |
228 | + if options.executable: |
229 | +@@ -619,7 +630,7 @@ Thank you for your understanding, and sorry for the inconvenience! |
230 | + update_bug = True |
231 | + if options.duplicate_db: |
232 | + crashdb.init_duplicate_db(options.duplicate_db) |
233 | +- res = crashdb.check_duplicate(int(crashid), report) |
234 | ++ res = crashdb.check_duplicate(crashid, report) |
235 | + if res: |
236 | + if res[1] is None: |
237 | + version = "not fixed yet" |
238 | +@@ -639,8 +650,8 @@ Thank you for your understanding, and sorry for the inconvenience! |
239 | + if "Stacktrace" in report: |
240 | + crashdb.update_traces(crashid, report) |
241 | + apport.log( |
242 | +- "New attachments uploaded to crash database LP: #" |
243 | +- + crashid, |
244 | ++ f"New attachments uploaded to crash database" |
245 | ++ f" LP: #{crashid}", |
246 | + options.timestamps, |
247 | + ) |
248 | + else: |
249 | diff --git a/debian/patches/fix-use-context-manager-when-manipulating-GzipFiles-LP-20.patch b/debian/patches/fix-use-context-manager-when-manipulating-GzipFiles-LP-20.patch |
250 | new file mode 100644 |
251 | index 0000000..9945554 |
252 | --- /dev/null |
253 | +++ b/debian/patches/fix-use-context-manager-when-manipulating-GzipFiles-LP-20.patch |
254 | @@ -0,0 +1,109 @@ |
255 | +From: Simon Chopin <simon.chopin@canonical.com> |
256 | +Date: Fri, 9 Feb 2024 11:25:13 +0100 |
257 | +Subject: fix: use context manager when manipulating GzipFiles (LP: #2051512) |
258 | + |
259 | +Those objects are technically file-like objects, with the associated |
260 | +semantics. In particular, starting with Python 3.12 the class using |
261 | +internal buffering, which means write() calls won't necessarily directly |
262 | +be reflected in the underlying storage object. |
263 | + |
264 | +For the sake of consistency, the context manager approach has been |
265 | +generalized to read() calls as well, except when we could get away with |
266 | +removing the use of GzipFile altogether. |
267 | + |
268 | +Origin: upstream, https://github.com/canonical/apport/pull/294 |
269 | +--- |
270 | + apport/crashdb_impl/launchpad.py | 3 ++- |
271 | + problem_report.py | 32 ++++++++++++++++---------------- |
272 | + tests/integration/test_problem_report.py | 3 ++- |
273 | + 3 files changed, 20 insertions(+), 18 deletions(-) |
274 | + |
275 | +diff --git a/apport/crashdb_impl/launchpad.py b/apport/crashdb_impl/launchpad.py |
276 | +index 6d7e8b4..9fb44fb 100644 |
277 | +--- a/apport/crashdb_impl/launchpad.py |
278 | ++++ b/apport/crashdb_impl/launchpad.py |
279 | +@@ -369,7 +369,8 @@ class CrashDatabase(apport.crashdb.CrashDatabase): |
280 | + pass |
281 | + elif ext == ".gz": |
282 | + try: |
283 | +- report[key] = gzip.GzipFile(fileobj=attachment).read() |
284 | ++ with gzip.GzipFile(fileobj=attachment) as gz: |
285 | ++ report[key] = gz.read() |
286 | + except OSError as error: |
287 | + # some attachments are only called .gz, but are |
288 | + # uncompressed (LP #574360) |
289 | +diff --git a/problem_report.py b/problem_report.py |
290 | +index 9b39a3b..5f65aad 100644 |
291 | +--- a/problem_report.py |
292 | ++++ b/problem_report.py |
293 | +@@ -65,7 +65,8 @@ class CompressedValue: |
294 | + def set_value(self, value): |
295 | + """Set uncompressed value.""" |
296 | + out = io.BytesIO() |
297 | +- gzip.GzipFile(self.name, mode="wb", fileobj=out, mtime=0).write(value) |
298 | ++ with gzip.GzipFile(self.name, mode="wb", fileobj=out, mtime=0) as gz: |
299 | ++ gz.write(value) |
300 | + self.gzipvalue = out.getvalue() |
301 | + self.legacy_zlib = False |
302 | + |
303 | +@@ -76,7 +77,7 @@ class CompressedValue: |
304 | + |
305 | + if self.legacy_zlib: |
306 | + return zlib.decompress(self.gzipvalue) |
307 | +- return gzip.GzipFile(fileobj=io.BytesIO(self.gzipvalue)).read() |
308 | ++ return gzip.decompress(self.gzipvalue) |
309 | + |
310 | + def write(self, file): |
311 | + """Write uncompressed value into given file-like object.""" |
312 | +@@ -86,12 +87,12 @@ class CompressedValue: |
313 | + file.write(zlib.decompress(self.gzipvalue)) |
314 | + return |
315 | + |
316 | +- gz = gzip.GzipFile(fileobj=io.BytesIO(self.gzipvalue)) |
317 | +- while True: |
318 | +- block = gz.read(1048576) |
319 | +- if not block: |
320 | +- break |
321 | +- file.write(block) |
322 | ++ with gzip.GzipFile(fileobj=io.BytesIO(self.gzipvalue)) as gz: |
323 | ++ while True: |
324 | ++ block = gz.read(1048576) |
325 | ++ if not block: |
326 | ++ break |
327 | ++ file.write(block) |
328 | + |
329 | + def __len__(self): |
330 | + """Return length of uncompressed value.""" |
331 | +@@ -635,14 +636,13 @@ class ProblemReport(collections.UserDict): |
332 | + attach_value = f.read() |
333 | + else: |
334 | + out = io.BytesIO() |
335 | +- gf = gzip.GzipFile(k, mode="wb", fileobj=out, mtime=0) |
336 | +- while True: |
337 | +- block = f.read(1048576) |
338 | +- if block: |
339 | +- gf.write(block) |
340 | +- else: |
341 | +- gf.close() |
342 | +- break |
343 | ++ with gzip.GzipFile(k, mode="wb", fileobj=out, mtime=0) as gz: |
344 | ++ while True: |
345 | ++ block = f.read(1048576) |
346 | ++ if block: |
347 | ++ gz.write(block) |
348 | ++ else: |
349 | ++ break |
350 | + attach_value = out.getvalue() |
351 | + f.close() |
352 | + |
353 | +diff --git a/tests/integration/test_problem_report.py b/tests/integration/test_problem_report.py |
354 | +index 7f705dd..f020021 100644 |
355 | +--- a/tests/integration/test_problem_report.py |
356 | ++++ b/tests/integration/test_problem_report.py |
357 | +@@ -537,4 +537,5 @@ class T(unittest.TestCase): |
358 | + with tempfile.TemporaryFile() as payload: |
359 | + payload.write(message.get_payload(decode=True)) |
360 | + payload.seek(0) |
361 | +- return gzip.GzipFile(mode="rb", fileobj=payload).read() |
362 | ++ with gzip.GzipFile(mode="rb", fileobj=payload) as gz: |
363 | ++ return gz.read() |
364 | diff --git a/debian/patches/retrace-directly-test-crashid-relevance.patch b/debian/patches/retrace-directly-test-crashid-relevance.patch |
365 | new file mode 100644 |
366 | index 0000000..92df430 |
367 | --- /dev/null |
368 | +++ b/debian/patches/retrace-directly-test-crashid-relevance.patch |
369 | @@ -0,0 +1,34 @@ |
370 | +From: Simon Chopin <simon.chopin@canonical.com> |
371 | +Date: Tue, 13 Feb 2024 20:05:37 +0100 |
372 | +Subject: retrace: directly test crashid relevance |
373 | + |
374 | +As it turns out testing for `options.report` being falsy meant that |
375 | +crashid was defined, but I'd rather test it directly as it's a bit |
376 | +cumbersome to trace (and it made pylint crazy). |
377 | + |
378 | +Origin: upstream, https://github.com/canonical/apport/pull/295 |
379 | +--- |
380 | + bin/apport-retrace | 3 +-- |
381 | + 1 file changed, 1 insertion(+), 2 deletions(-) |
382 | + |
383 | +diff --git a/bin/apport-retrace b/bin/apport-retrace |
384 | +index 246a065..f12f8da 100755 |
385 | +--- a/bin/apport-retrace |
386 | ++++ b/bin/apport-retrace |
387 | +@@ -345,7 +345,6 @@ def load_report( |
388 | + crashid = int(options.report) |
389 | + report = crashdb.download(crashid) |
390 | + apport.memdbg("downloaded report from crash DB") |
391 | +- options.report = None |
392 | + return report, crashid |
393 | + except AssertionError as error: |
394 | + if "apport format data" in str(error): |
395 | +@@ -619,7 +618,7 @@ Thank you for your understanding, and sorry for the inconvenience! |
396 | + modified = True |
397 | + |
398 | + if modified: |
399 | +- if not options.report and not options.output: |
400 | ++ if crashid is not None and not options.output: |
401 | + if not options.auth: |
402 | + apport.fatal( |
403 | + "You need to specify --auth for uploading retraced results" |
404 | diff --git a/debian/patches/series b/debian/patches/series |
405 | index 1c3393f..e03ab4b 100644 |
406 | --- a/debian/patches/series |
407 | +++ b/debian/patches/series |
408 | @@ -11,3 +11,6 @@ setup-replace-distutils.log-by-logging-library.patch |
409 | setup-drop-DistUtilsExtra-version-check.patch |
410 | setup-explicitly-list-packages-to-install.patch |
411 | setup-port-build_java-to-setuptools.patch |
412 | +apport-retrace-split-out-report-loading-into-its-own-func.patch |
413 | +retrace-directly-test-crashid-relevance.patch |
414 | +fix-use-context-manager-when-manipulating-GzipFiles-LP-20.patch |
415 | diff --git a/debian/rules b/debian/rules |
416 | index 0acdb99..b38b9fd 100755 |
417 | --- a/debian/rules |
418 | +++ b/debian/rules |
419 | @@ -3,21 +3,6 @@ |
420 | %: |
421 | dh "$@" --with python3,translations --buildsystem pybuild |
422 | |
423 | -# needs manual commands for Python 3, see Debian #597105 |
424 | -override_dh_auto_clean: |
425 | - dh_auto_clean |
426 | - rm -rf build |
427 | - |
428 | -override_dh_auto_build: |
429 | - dh_auto_build |
430 | - python3 setup.py build |
431 | - |
432 | -override_dh_auto_install: |
433 | - dh_auto_install |
434 | - set -ex; for python in $(shell py3versions -r); do \ |
435 | - $$python setup.py install --root=$(CURDIR)/debian/tmp --install-layout=deb; \ |
436 | - done |
437 | - |
438 | override_dh_auto_test: |
439 | ifeq (, $(findstring nocheck, $(DEB_BUILD_OPTIONS))) |
440 | tests/run-linters --errors-only |
441 | @@ -25,7 +10,7 @@ ifeq (, $(findstring nocheck, $(DEB_BUILD_OPTIONS))) |
442 | endif |
443 | |
444 | override_dh_installinit: |
445 | - dh_installinit --error-handler=true |
446 | + dh_installinit -papport-core-dump-handler --name=apport --error-handler=true --only-scripts |
447 | |
448 | override_dh_install: |
449 | dh_install -X.pyc -X.egg-info |
LGTM except for the circular deps thing, which should really not be happening.