blob: 20f0e63f6af8fc55d292d4846acccfa786c20906 [file] [log] [blame]
Mike Frysingerf1ba7ad2022-09-12 05:42:57 -04001# Copyright 2013 The ChromiumOS Authors
Mike Frysinger69cb41d2013-08-11 20:08:19 -04002# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Generate minidump symbols for use by the Crash server.
6
7Note: This should be run inside the chroot.
8
9This produces files in the breakpad format required by minidump_stackwalk and
10the crash server to dump stack information.
11
12Basically it scans all the split .debug files in /build/$BOARD/usr/lib/debug/
13and converts them over using the `dump_syms` programs. Those plain text .sym
14files are then stored in /build/$BOARD/usr/lib/debug/breakpad/.
15
Mike Frysinger02e1e072013-11-10 22:11:34 -050016If you want to actually upload things, see upload_symbols.py.
17"""
Mike Frysinger69cb41d2013-08-11 20:08:19 -040018
19import collections
20import ctypes
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -080021import enum
Chris McDonaldb55b7032021-06-17 16:41:32 -060022import logging
Mike Frysinger69cb41d2013-08-11 20:08:19 -040023import multiprocessing
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -080024import multiprocessing.sharedctypes
Mike Frysinger69cb41d2013-08-11 20:08:19 -040025import os
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -080026import re
27from typing import Optional
Mike Frysinger69cb41d2013-08-11 20:08:19 -040028
Chris McDonaldb55b7032021-06-17 16:41:32 -060029from chromite.cbuildbot import cbuildbot_alerts
Mike Frysinger06a51c82021-04-06 11:39:17 -040030from chromite.lib import build_target_lib
Mike Frysinger69cb41d2013-08-11 20:08:19 -040031from chromite.lib import commandline
32from chromite.lib import cros_build_lib
33from chromite.lib import osutils
34from chromite.lib import parallel
Mike Frysinger96ad3f22014-04-24 23:27:27 -040035from chromite.lib import signals
Alex Klein1809f572021-09-09 11:28:37 -060036from chromite.utils import file_util
Mike Frysinger69cb41d2013-08-11 20:08:19 -040037
Mike Frysinger807d8282022-04-28 22:45:17 -040038
Stephen Boydfc1c8032021-10-06 20:58:37 -070039# Elf files that don't exist but have a split .debug file installed.
40ALLOWED_DEBUG_ONLY_FILES = {
Alex Klein1699fab2022-09-08 08:46:06 -060041 "boot/vmlinux",
Stephen Boydfc1c8032021-10-06 20:58:37 -070042}
Mike Frysinger69cb41d2013-08-11 20:08:19 -040043
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -080044# Allowlist of elf files that we know we can't symbolize in the normal way, but
45# which we don't have an automatic way to detect.
46EXPECTED_POOR_SYMBOLIZATION_FILES = ALLOWED_DEBUG_ONLY_FILES | {
47 # Git binaries are downloaded as binary blobs already stripped.
48 "usr/bin/git",
49 "usr/bin/git-receive-pack",
50 "usr/bin/git-upload-archive",
51 "usr/bin/git-upload-pack",
52 # Prebuild Android binary
53 "build/rootfs/opt/google/vms/android/etc/bin/XkbToKcmConverter",
Ian Barkley-Yeungab1dab12023-04-21 18:19:55 -070054 "build/rootfs/opt/google/containers/android/etc/bin/XkbToKcmConverter",
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -080055 # Pulled from
56 # https://skia.googlesource.com/buildbot/+/refs/heads/main/gold-client/, no
57 # need to resymbolize.
58 "usr/bin/goldctl",
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -080059}
60
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -080061# Allowlist of patterns for ELF files that symbolize (dump_syms exits with
62# success) but don't pass symbol file validation. Note that ELFs listed in
63# EXPECTED_POOR_SYMBOLIZATION_FILES do not have their symbol files validated and
64# do not need to be repeated here.
65ALLOWLIST_NO_SYMBOL_FILE_VALIDATION = {
66 # Built in a weird way, see comments at top of
67 # https://source.chromium.org/chromium/chromium/src/+/main:native_client/src/trusted/service_runtime/linux/nacl_bootstrap.x
68 "opt/google/chrome/nacl_helper_bootstrap",
69 # TODO(b/273543528): Investigate why this doesn't have stack records on
70 # kevin builds.
71 "build/rootfs/dlc-scaled/screen-ai/package/root/libchromescreenai.so",
Ian Barkley-Yeungab1dab12023-04-21 18:19:55 -070072 # TODO(b/279645511): Investigate why this doesn't have STACK records on
73 # jacuzzi, scarlet, kukui, etc.
74 "usr/bin/rma_reset",
75 # Virtual dynamic shared object, not expected to have STACK records.
Trent Aptedc4f366a2023-05-16 15:32:48 +100076 (
77 "opt/google/containers/android/ndk_translation/lib/arm/"
78 "libndk_translation_vdso.so"
79 ),
Ian Barkley-Yeungab1dab12023-04-21 18:19:55 -070080 # TODO(b/279665879): Figure out why this ndk_translation libraries is not
81 # getting STACK records.
82 "opt/google/containers/android/ndk_translation/lib/arm/libdexfile.so",
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -080083}
84# Same but patterns not exact paths.
85ALLOWLIST_NO_SYMBOL_FILE_VALIDATION_RE = tuple(
86 re.compile(x)
87 for x in (
88 # Only has code if use.camera_feature_effects. Otherwise will have no
89 # STACK records.
90 r"usr/lib[^/]*/libcros_ml_core\.so",
91 # Prebuilt closed-source library.
92 r"usr/lib[^/]*/python[0-9]\.[0-9]/site-packages/.*/x_ignore_nofocus.so",
93 # b/273577373: Rarely used, and only by few programs that do
94 # non-standard encoding conversions
95 r"lib[^/]*/libnss_files\.so\.[0-9.]+",
96 r"lib[^/]*/libnss_dns\.so\.[0-9.]+",
97 r"usr/lib[^/]*/gconv/libISOIR165\.so",
98 r"usr/lib[^/]*/gconv/libGB\.so",
99 r"usr/lib[^/]*/gconv/libKSC\.so",
100 r"usr/lib[^/]*/gconv/libCNS\.so",
101 r"usr/lib[^/]*/gconv/libJISX0213\.so",
102 r"usr/lib[^/]*/gconv/libJIS\.so",
103 # TODO(b/273579075): Figure out why libcares.so is not getting STACK
104 # records on kevin.
105 r"usr/lib[^/]*/libcares\.so[0-9.]*",
106 # libevent-2.1.so.7.0.1 does not have executable code and thus no
107 # STACK records. See libevent-2.1.12-libevent-shrink.patch.
108 r"usr/lib[^/]*/libevent-[0-9.]+\.so[0-9.]*",
109 # TODO(b/273599604): Figure out why libdcerpc-samr.so.0.0.1 is not
110 # getting STACK records on kevin.
111 r"usr/lib[^/]*/libdcerpc-samr\.so[0-9.]*",
112 # TODO(b/272613635): Figure out why these libabsl shared libraries are
113 # not getting STACK records.
114 r"usr/lib[^/]*/libabsl_bad_variant_access\.so\.[0-9.]+",
115 r"usr/lib[^/]*/libabsl_random_internal_platform\.so\.[0-9.]+",
116 r"usr/lib[^/]*/libabsl_flags\.so\.[0-9.]+",
117 r"usr/lib[^/]*/libabsl_bad_any_cast_impl\.so\.[0-9.]+",
118 r"usr/lib[^/]*/libabsl_bad_optional_access\.so\.[0-9.]+",
119 # TODO(b/273607289): Figure out why libgrpc++_error_details.so.1.43.0 is
120 # not getting STACK records on kevin.
121 r"usr/lib[^/]*/libgrpc\+\+_error_details\.so\.[0-9.]+",
Ian Barkley-Yeungab1dab12023-04-21 18:19:55 -0700122 # linux-gate.so is system call wrappers which don't always have enough
123 # code to get STACK entries.
124 r"lib/modules/[^/]+/vdso/linux-gate\.so",
125 # TODO(b/280503615): Figure out why libGLESv2.so.2.0.0 doesn't have
126 # STACK records on amd64-generic or betty-pi-arc.
127 r"usr/lib[^/]*/libGLESv[0-9]+\.so.*",
128 # This is just a backwards compatibility stub if ENABLE_HLSL is defined,
129 # see https://github.com/KhronosGroup/glslang/blob/main/hlsl/stub.cpp
130 r"usr/lib[^/]*/libHLSL.so",
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -0800131 )
132)
133
Alex Klein1699fab2022-09-08 08:46:06 -0600134SymbolHeader = collections.namedtuple(
135 "SymbolHeader",
136 (
137 "cpu",
138 "id",
139 "name",
140 "os",
141 ),
142)
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400143
144
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -0800145class SymbolGenerationResult(enum.Enum):
146 """Result of running dump_syms
147
148 Return value of _DumpAllowingBasicFallback() and _DumpExpectingSymbols().
149 """
150
151 SUCCESS = 1
152 UNEXPECTED_FAILURE = 2
153 EXPECTED_FAILURE = 3
154
155
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -0800156class ExpectedFiles(enum.Enum):
157 """The files always expect to see dump_syms run on.
158
159 We do extra validation on a few, semi-randomly chosen files. If we do not
160 create symbol files for these ELFs, something is very wrong.
161 """
162
163 ASH_CHROME = enum.auto()
164 LIBC = enum.auto()
165 CRASH_REPORTER = enum.auto()
166 LIBMETRICS = enum.auto()
167
168
169ALL_EXPECTED_FILES = frozenset(
170 (
171 ExpectedFiles.ASH_CHROME,
172 ExpectedFiles.LIBC,
173 ExpectedFiles.CRASH_REPORTER,
174 ExpectedFiles.LIBMETRICS,
175 )
176)
177
178
Ian Barkley-Yeungab1dab12023-04-21 18:19:55 -0700179# Regular expression for ChromeOS's libc.so. Note that some containers have
180# their own libc.so file; we don't want to do the extra validation on those.
181# (They are often subsets of the full libc and will not pass STACK count tests.)
182LIBC_REGEX = re.compile(r"lib[^/]*/libc\.so\.[0-9.]+")
183
184
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -0800185class SymbolFileLineCounts:
186 """Counts of the various types of lines in a .sym file"""
187
188 LINE_NUMBER_REGEX = re.compile(r"^([0-9a-f]+)")
189
190 def __init__(self, sym_file: str, elf_file: str):
191 # https://chromium.googlesource.com/breakpad/breakpad/+/HEAD/docs/symbol_files.md
192 # explains what these line types are.
193 self.module_lines = 0
194 self.file_lines = 0
195 self.inline_origin_lines = 0
196 self.func_lines = 0
197 self.inline_lines = 0
198 self.line_number_lines = 0
199 self.public_lines = 0
200 self.stack_lines = 0
201 # Not listed in the documentation but still present.
202 self.info_lines = 0
203
204 with open(sym_file, mode="r", encoding="utf-8") as f:
205 for line in f:
206 words = line.split()
207 expected_words_max = None
208 if not words:
209 raise ValueError(
210 f"{elf_file}: symbol file has unexpected blank line"
211 )
212
213 line_type = words[0]
214 if line_type == "MODULE":
215 self.module_lines += 1
216 expected_words_min = 5
217 expected_words_max = 5
218 elif line_type == "FILE":
219 self.file_lines += 1
220 expected_words_min = 3
221 # No max, filenames can have spaces.
222 elif line_type == "INLINE_ORIGIN":
223 self.inline_origin_lines += 1
224 expected_words_min = 3
225 # No max, function parameter lists can have spaces.
226 elif line_type == "FUNC":
227 self.func_lines += 1
228 expected_words_min = 5
229 # No max, function parameter lists can have spaces.
230 elif line_type == "INLINE":
231 self.inline_lines += 1
232 expected_words_min = 5
233 # No max, INLINE can have multiple address pairs.
234 elif SymbolFileLineCounts.LINE_NUMBER_REGEX.match(line_type):
235 self.line_number_lines += 1
236 expected_words_min = 4
237 expected_words_max = 4
238 line_type = "line number"
239 elif line_type == "PUBLIC":
240 self.public_lines += 1
Ian Barkley-Yeungab1dab12023-04-21 18:19:55 -0700241 # TODO(b/251003272): expected_words_min should be 4;
242 # however, dump_syms sometimes produces PUBLIC records with
243 # no symbol name. This is an error but is not affecting our
244 # ability to decode stacks.
245 expected_words_min = 3
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -0800246 # No max, function parameter lists can have spaces.
247 elif line_type == "STACK":
248 self.stack_lines += 1
249 expected_words_min = 5
250 # No max, expressions can be complex.
251 elif line_type == "INFO":
252 self.info_lines += 1
253 # Not documented, so unclear what the min & max are
254 expected_words_min = None
255 else:
256 raise ValueError(
257 f"{elf_file}: symbol file has unknown line type "
Ian Barkley-Yeungccfa1b52023-07-07 18:55:46 -0700258 f"{line_type} (line='{line}')"
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -0800259 )
260
261 if expected_words_max is not None:
262 if not (
263 expected_words_min <= len(words) <= expected_words_max
264 ):
265 raise ValueError(
266 f"{elf_file}: symbol file has {line_type} line "
267 f"with {len(words)} words (expected "
Ian Barkley-Yeungccfa1b52023-07-07 18:55:46 -0700268 f"{expected_words_min} - {expected_words_max}) "
269 f"(line='{line}')"
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -0800270 )
271 elif expected_words_min is not None:
272 if len(words) < expected_words_min:
273 raise ValueError(
274 f"{elf_file}: symbol file has {line_type} line "
275 f"with {len(words)} words (expected "
Ian Barkley-Yeungccfa1b52023-07-07 18:55:46 -0700276 f"{expected_words_min} or more) (line='{line}')"
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -0800277 )
278
279
280def ValidateSymbolFile(
281 sym_file: str,
282 elf_file: str,
283 sysroot: Optional[str],
284 found_files: Optional[multiprocessing.managers.ListProxy],
285) -> bool:
286 """Checks that the given sym_file has enough info for us to get good stacks.
287
288 Validates that the given sym_file has enough information for us to get
289 good error reports -- enough STACK records to unwind the stack and enough
290 FUNC or PUBLIC records to turn the function addresses into human-readable
291 names.
292
293 Args:
294 sym_file: The complete path to the breakpad symbol file to validate
295 elf_file: The complete path to the elf file which was the source of the
296 symbol file.
297 sysroot: If not None, the root of the build directory ('/build/eve', for
298 instance).
299 found_files: A multiprocessing.managers.ListProxy list containing
300 ExpectedFiles, representing which of the "should always be present"
301 files have been processed.
302
303 Returns:
304 True if the symbol file passes validation.
305 """
306 if sysroot is not None:
307 relative_path = os.path.relpath(elf_file, sysroot)
308 else:
309 relative_path = os.path.relpath(elf_file, "/")
310
311 if relative_path in ALLOWLIST_NO_SYMBOL_FILE_VALIDATION:
312 return True
313 for regex in ALLOWLIST_NO_SYMBOL_FILE_VALIDATION_RE:
314 if regex.match(relative_path):
315 return True
316
317 counts = SymbolFileLineCounts(sym_file, elf_file)
318
319 errors = False
320 if counts.stack_lines == 0:
321 # Use the elf_file in error messages; sym_file is still a temporary
322 # file with a meaningless-to-humans name right now.
323 logging.warning("%s: Symbol file has no STACK records", elf_file)
324 errors = True
325 if counts.module_lines != 1:
326 logging.warning(
327 "%s: Symbol file has %d MODULE lines", elf_file, counts.module_lines
328 )
329 errors = True
330 # Many shared object files have only PUBLIC functions. In theory,
331 # executables should always have at least one FUNC (main) and some line
332 # numbers, but for reasons I'm unclear on, C-based executables often just
333 # have PUBLIC records. dump_syms does not support line numbers after
334 # PUBLIC records, only FUNC records, so such executables will also have
335 # no line numbers.
336 if counts.public_lines == 0 and counts.func_lines == 0:
337 logging.warning(
338 "%s: Symbol file has no FUNC or PUBLIC records", elf_file
339 )
340 errors = True
341 # However, if we get a FUNC record, we do want line numbers for it.
342 if counts.func_lines > 0 and counts.line_number_lines == 0:
343 logging.warning(
344 "%s: Symbol file has FUNC records but no line numbers", elf_file
345 )
346 errors = True
347
348 if counts.line_number_lines > 0 and counts.file_lines == 0:
349 logging.warning(
350 "%s: Symbol file has line number records but no FILE records",
351 elf_file,
352 )
353 errors = True
354 if counts.inline_lines > 0 and counts.file_lines == 0:
355 logging.warning(
356 "%s: Symbol file has INLINE records but no FILE records", elf_file
357 )
358 errors = True
359
360 if counts.inline_lines > 0 and counts.inline_origin_lines == 0:
361 logging.warning(
362 "%s: Symbol file has INLINE records but no INLINE_ORIGIN records",
363 elf_file,
364 )
365 errors = True
366
367 def _AddFoundFile(files, found):
368 """Add another file to the list of expected files we've found."""
369 if files is not None:
370 files.append(found)
371
372 # Extra validation for a few ELF files which are special. Either these are
373 # unusually important to the system (chrome binary, which is where a large
374 # fraction of our crashes occur, and libc.so, which is in every stack), or
375 # they are some hand-chosen ELF files which stand in for "normal" platform2
376 # binaries. Not all ELF files would pass the extra validation, so we can't
377 # run these checks on every ELF, but we want to make sure we don't end up
378 # with, say, a chrome build or a platform2 build with just one or two FUNC
379 # records on every binary.
380 if relative_path == "opt/google/chrome/chrome":
381 _AddFoundFile(found_files, ExpectedFiles.ASH_CHROME)
382 if counts.func_lines < 100000:
383 logging.warning(
384 "chrome should have at least 100,000 FUNC records, found %d",
385 counts.func_lines,
386 )
387 errors = True
388 if counts.stack_lines < 1000000:
389 logging.warning(
390 "chrome should have at least 1,000,000 STACK records, found %d",
391 counts.stack_lines,
392 )
393 errors = True
394 if counts.line_number_lines < 1000000:
395 logging.warning(
396 "chrome should have at least 1,000,000 line number records, "
397 "found %d",
398 counts.line_number_lines,
399 )
400 errors = True
401 # Lacros symbol files are not generated as part of the ChromeOS build and
402 # can't be validated here.
403 # TODO(b/273836486): Add similar logic to the code that generates Lacros
404 # symbols.
Ian Barkley-Yeungab1dab12023-04-21 18:19:55 -0700405 elif LIBC_REGEX.fullmatch(relative_path):
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -0800406 _AddFoundFile(found_files, ExpectedFiles.LIBC)
407 if counts.public_lines < 100:
408 logging.warning(
409 "%s should have at least 100 PUBLIC records, found %d",
410 elf_file,
411 counts.public_lines,
412 )
413 errors = True
414 if counts.stack_lines < 10000:
415 logging.warning(
416 "%s should have at least 10000 STACK records, found %d",
417 elf_file,
418 counts.stack_lines,
419 )
420 errors = True
421 elif relative_path == "sbin/crash_reporter":
422 # Representative platform2 executable.
423 _AddFoundFile(found_files, ExpectedFiles.CRASH_REPORTER)
424 if counts.stack_lines < 1000:
425 logging.warning(
426 "crash_reporter should have at least 1000 STACK records, "
427 "found %d",
428 counts.stack_lines,
429 )
430 errors = True
431 if counts.func_lines < 1000:
432 logging.warning(
433 "crash_reporter should have at least 1000 FUNC records, "
434 "found %d",
435 counts.func_lines,
436 )
437 errors = True
438 if counts.line_number_lines < 10000:
439 logging.warning(
440 "crash_reporter should have at least 10,000 line number "
441 "records, found %d",
442 counts.line_number_lines,
443 )
444 errors = True
445 elif os.path.basename(relative_path) == "libmetrics.so":
446 # Representative platform2 shared library.
447 _AddFoundFile(found_files, ExpectedFiles.LIBMETRICS)
448 if counts.func_lines < 100:
449 logging.warning(
450 "libmetrics should have at least 100 FUNC records, found %d",
451 counts.func_lines,
452 )
453 errors = True
454 if counts.public_lines == 0:
455 logging.warning(
456 "libmetrics should have at least 1 PUBLIC record, found %d",
457 counts.public_lines,
458 )
459 errors = True
460 if counts.stack_lines < 1000:
461 logging.warning(
462 "libmetrics should have at least 1000 STACK records, found %d",
463 counts.stack_lines,
464 )
465 errors = True
466 if counts.line_number_lines < 5000:
467 logging.warning(
468 "libmetrics should have at least 5000 line number records, "
469 "found %d",
470 counts.line_number_lines,
471 )
472 errors = True
473
474 return not errors
475
476
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -0800477def _ExpectGoodSymbols(elf_file, sysroot):
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -0800478 """Determines if we expect dump_syms to create good symbols.
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -0800479
480 We know that certain types of files never generate good symbols. Distinguish
481 those from the majority of elf files which should generate good symbols.
482
483 Args:
484 elf_file: The complete path to the file which we will pass to dump_syms
485 sysroot: If not None, the root of the build directory ('/build/eve', for
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -0800486 instance)
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -0800487
488 Returns:
489 True if the elf file should generate good symbols, False if not.
490 """
491 # .ko files (kernel object files) never produce good symbols.
492 if elf_file.endswith(".ko"):
493 return False
494
495 # dump_syms doesn't understand Golang executables.
496 result = cros_build_lib.run(
497 ["/usr/bin/file", elf_file], print_cmd=False, stdout=True
498 )
499 if b"Go BuildID" in result.stdout:
500 return False
501
502 if sysroot is not None:
503 relative_path = os.path.relpath(elf_file, sysroot)
504 else:
Ian Barkley-Yeunge1785762023-03-08 18:32:18 -0800505 relative_path = os.path.relpath(elf_file, "/")
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -0800506
507 if relative_path in EXPECTED_POOR_SYMBOLIZATION_FILES:
508 return False
509
Ian Barkley-Yeunge1785762023-03-08 18:32:18 -0800510 # Binaries in /usr/local are not actually shipped to end-users, so we
511 # don't care if they get good symbols -- we should never get crash reports
512 # for them anyways.
513 if relative_path.startswith("usr/local"):
514 return False
515
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -0800516 return True
517
518
519def ReadSymsHeader(sym_file, name_for_errors):
Alex Klein1699fab2022-09-08 08:46:06 -0600520 """Parse the header of the symbol file
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400521
Alex Klein1699fab2022-09-08 08:46:06 -0600522 The first line of the syms file will read like:
Alex Klein8b444532023-04-11 16:35:24 -0600523 MODULE Linux arm F4F6FA6CCBDEF455039C8DE869C8A2F40 blkid
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400524
Alex Klein1699fab2022-09-08 08:46:06 -0600525 https://code.google.com/p/google-breakpad/wiki/SymbolFiles
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400526
Alex Klein1699fab2022-09-08 08:46:06 -0600527 Args:
Alex Klein8b444532023-04-11 16:35:24 -0600528 sym_file: The symbol file to parse
529 name_for_errors: A name for error strings. Can be the name of the elf
530 file that generated the symbol file, or the name of the symbol file
531 if the symbol file has already been moved to a meaningful location.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500532
Alex Klein1699fab2022-09-08 08:46:06 -0600533 Returns:
Alex Klein8b444532023-04-11 16:35:24 -0600534 A SymbolHeader object
Mike Frysinger1a736a82013-12-12 01:50:59 -0500535
Alex Klein1699fab2022-09-08 08:46:06 -0600536 Raises:
Alex Klein8b444532023-04-11 16:35:24 -0600537 ValueError if the first line of |sym_file| is invalid
Alex Klein1699fab2022-09-08 08:46:06 -0600538 """
539 with file_util.Open(sym_file, "rb") as f:
540 header = f.readline().decode("utf-8").split()
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400541
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -0800542 if len(header) != 5 or header[0] != "MODULE":
543 raise ValueError(
544 f"header of sym file from {name_for_errors} is invalid"
545 )
Mike Frysinger50cedd32014-02-09 23:03:18 -0500546
Alex Klein1699fab2022-09-08 08:46:06 -0600547 return SymbolHeader(
548 os=header[1], cpu=header[2], id=header[3], name=header[4]
549 )
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400550
551
Alex Klein1699fab2022-09-08 08:46:06 -0600552def GenerateBreakpadSymbol(
553 elf_file,
554 debug_file=None,
555 breakpad_dir=None,
556 strip_cfi=False,
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -0800557 sysroot=None,
Alex Klein1699fab2022-09-08 08:46:06 -0600558 num_errors=None,
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -0800559 found_files=None,
Alex Klein1699fab2022-09-08 08:46:06 -0600560 dump_syms_cmd="dump_syms",
David Rileyc1a44f62023-07-07 16:18:34 -0700561 dump_syms_args=None,
Ian Barkley-Yeungb5274442023-04-28 16:32:20 -0700562 force_basic_fallback=False,
Alex Klein1699fab2022-09-08 08:46:06 -0600563):
564 """Generate the symbols for |elf_file| using |debug_file|
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400565
Alex Klein1699fab2022-09-08 08:46:06 -0600566 Args:
Alex Klein8b444532023-04-11 16:35:24 -0600567 elf_file: The file to dump symbols for
568 debug_file: Split debug file to use for symbol information
569 breakpad_dir: The dir to store the output symbol file in
570 strip_cfi: Do not generate CFI data
571 sysroot: Path to the sysroot with the elf_file under it
572 num_errors: An object to update with the error count (needs a .value
573 member).
574 found_files: A multiprocessing.managers.ListProxy list containing
575 ExpectedFiles, representing which of the "should always be present"
576 files have been processed.
577 dump_syms_cmd: Command to use for dumping symbols.
David Rileyc1a44f62023-07-07 16:18:34 -0700578 dump_syms_args: List of args to pass to dump_syms_cmd.
579 If not specified, a reasonable default will be used.
Ian Barkley-Yeungb5274442023-04-28 16:32:20 -0700580 force_basic_fallback: If True, always use _DumpAllowingBasicFallback()
581 instead of _DumpExpectingSymbols().
Mike Frysinger1a736a82013-12-12 01:50:59 -0500582
Alex Klein1699fab2022-09-08 08:46:06 -0600583 Returns:
Alex Klein8b444532023-04-11 16:35:24 -0600584 The name of symbol file written out on success, or the failure count.
Alex Klein1699fab2022-09-08 08:46:06 -0600585 """
586 assert breakpad_dir
587 if num_errors is None:
Trent Apted1e2e4f32023-05-05 03:50:20 +0000588 num_errors = ctypes.c_int(0)
David Rileyc1a44f62023-07-07 16:18:34 -0700589 if dump_syms_args is None:
590 dump_syms_args = ["-v", "-d", "-m"]
Alex Klein1699fab2022-09-08 08:46:06 -0600591 debug_file_only = not os.path.exists(elf_file)
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400592
David Rileyc1a44f62023-07-07 16:18:34 -0700593 cmd_base = [dump_syms_cmd] + dump_syms_args
Alex Klein1699fab2022-09-08 08:46:06 -0600594 if strip_cfi:
595 cmd_base += ["-c"]
596 # Some files will not be readable by non-root (e.g. set*id /bin/su).
597 needs_sudo = not os.access(elf_file, os.R_OK)
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400598
Alex Klein1699fab2022-09-08 08:46:06 -0600599 def _DumpIt(cmd_args):
600 if needs_sudo:
601 run_command = cros_build_lib.sudo_run
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400602 else:
Alex Klein1699fab2022-09-08 08:46:06 -0600603 run_command = cros_build_lib.run
604 return run_command(
605 cmd_base + cmd_args,
606 stderr=True,
607 stdout=temp.name,
608 check=False,
609 debug_level=logging.DEBUG,
610 )
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400611
Alex Klein1699fab2022-09-08 08:46:06 -0600612 def _CrashCheck(result, file_or_files, msg):
613 if result.returncode:
614 cbuildbot_alerts.PrintBuildbotStepWarnings()
615 if result.returncode < 0:
616 logging.warning(
617 "dump_syms %s crashed with %s; %s",
618 file_or_files,
619 signals.StrSignal(-result.returncode),
620 msg,
621 )
622 else:
623 logging.warning(
624 "dump_syms %s returned %d; %s",
625 file_or_files,
626 result.returncode,
627 msg,
628 )
629 logging.warning("output:\n%s", result.stderr.decode("utf-8"))
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400630
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -0800631 def _DumpAllowingBasicFallback():
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -0800632 """Dump symbols for an ELF when we do NOT expect to get good symbols.
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -0800633
634 Returns:
635 A SymbolGenerationResult
636 """
Alex Klein1699fab2022-09-08 08:46:06 -0600637 if debug_file:
638 # Try to dump the symbols using the debug file like normal.
639 if debug_file_only:
640 cmd_args = [debug_file]
641 file_or_files = debug_file
642 else:
643 cmd_args = [elf_file, os.path.dirname(debug_file)]
644 file_or_files = [elf_file, debug_file]
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400645
Alex Klein1699fab2022-09-08 08:46:06 -0600646 result = _DumpIt(cmd_args)
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400647
Alex Klein1699fab2022-09-08 08:46:06 -0600648 if result.returncode:
649 # Sometimes dump_syms can crash because there's too much info.
650 # Try dumping and stripping the extended stuff out. At least
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -0800651 # this way we'll get the extended symbols.
652 # https://crbug.com/266064
Alex Klein1699fab2022-09-08 08:46:06 -0600653 _CrashCheck(result, file_or_files, "retrying w/out CFI")
654 cmd_args = ["-c", "-r"] + cmd_args
655 result = _DumpIt(cmd_args)
656 _CrashCheck(result, file_or_files, "retrying w/out debug")
Prathmesh Prabhu9995e9b2013-10-31 16:43:55 -0700657
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -0800658 if not result.returncode:
659 return SymbolGenerationResult.SUCCESS
Prathmesh Prabhu9995e9b2013-10-31 16:43:55 -0700660
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -0800661 # If that didn't work (no debug, or dump_syms still failed), try
662 # dumping just the file itself directly.
663 result = _DumpIt([elf_file])
664 if result.returncode:
665 # A lot of files (like kernel files) contain no debug information,
666 # do not consider such occurrences as errors.
667 cbuildbot_alerts.PrintBuildbotStepWarnings()
668 if b"file contains no debugging information" in result.stderr:
669 logging.warning("dump_syms failed; giving up entirely.")
670 logging.warning("No symbols found for %s", elf_file)
671 return SymbolGenerationResult.EXPECTED_FAILURE
672 else:
673 _CrashCheck(result, elf_file, "counting as failure")
674 return SymbolGenerationResult.UNEXPECTED_FAILURE
675
676 return SymbolGenerationResult.SUCCESS
677
678 def _DumpExpectingSymbols():
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -0800679 """Dump symbols for an ELF when we expect to get good symbols.
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -0800680
681 Returns:
682 A SymbolGenerationResult. We never expect failure, so the result
683 will always be SUCCESS or UNEXPECTED_FAILURE.
684 """
685 if not debug_file:
686 logging.warning("%s must have debug file", elf_file)
687 return SymbolGenerationResult.UNEXPECTED_FAILURE
688
689 cmd_args = [elf_file, os.path.dirname(debug_file)]
690 result = _DumpIt(cmd_args)
691 if result.returncode:
Ian Barkley-Yeungab1dab12023-04-21 18:19:55 -0700692 _CrashCheck(
693 result,
694 [elf_file, debug_file],
695 "unexpected symbol generation failure",
696 )
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -0800697 return SymbolGenerationResult.UNEXPECTED_FAILURE
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -0800698
699 # TODO(b/270240549): Remove try/except, allow exceptions to just
700 # fail the script. The try/except is just here until we are sure this
701 # will not break the build.
702 try:
703 if not ValidateSymbolFile(
704 temp.name, elf_file, sysroot, found_files
705 ):
706 logging.warning("%s: symbol file failed validation", elf_file)
707 return SymbolGenerationResult.UNEXPECTED_FAILURE
708 except ValueError as e:
709 logging.warning(
710 "%s: symbol file failed validation due to exception %s",
711 elf_file,
712 e,
713 )
714 return SymbolGenerationResult.UNEXPECTED_FAILURE
715
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -0800716 return SymbolGenerationResult.SUCCESS
717
718 osutils.SafeMakedirs(breakpad_dir)
719 with cros_build_lib.UnbufferedNamedTemporaryFile(
720 dir=breakpad_dir, delete=False
721 ) as temp:
Ian Barkley-Yeungb5274442023-04-28 16:32:20 -0700722 if not force_basic_fallback and _ExpectGoodSymbols(elf_file, sysroot):
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -0800723 result = _DumpExpectingSymbols()
724 # Until the EXPECTED_POOR_SYMBOLIZATION_FILES allowlist is
725 # completely set up for all boards, don't fail the build if
726 # _ExpectGoodSymbols is wrong.
727 # TODO(b/241470012): Remove the call to _DumpAllowingBasicFallback()
728 # and just error out if _DumpExpectingSymbols fails.
729 if result == SymbolGenerationResult.UNEXPECTED_FAILURE:
730 result = _DumpAllowingBasicFallback()
731 else:
732 result = _DumpAllowingBasicFallback()
733
734 if result == SymbolGenerationResult.UNEXPECTED_FAILURE:
735 num_errors.value += 1
736 os.unlink(temp.name)
737 return num_errors.value
738
739 if result == SymbolGenerationResult.EXPECTED_FAILURE:
740 os.unlink(temp.name)
741 return 0
Prathmesh Prabhu9995e9b2013-10-31 16:43:55 -0700742
Alex Klein1699fab2022-09-08 08:46:06 -0600743 # Move the dumped symbol file to the right place:
Alex Klein8b444532023-04-11 16:35:24 -0600744 # /$SYSROOT/usr/lib/debug/breakpad/<module-name>/<id>/<module-name>.sym
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -0800745 header = ReadSymsHeader(temp, elf_file)
Alex Klein1699fab2022-09-08 08:46:06 -0600746 logging.info("Dumped %s as %s : %s", elf_file, header.name, header.id)
747 sym_file = os.path.join(
748 breakpad_dir, header.name, header.id, header.name + ".sym"
749 )
750 osutils.SafeMakedirs(os.path.dirname(sym_file))
751 os.rename(temp.name, sym_file)
752 os.chmod(sym_file, 0o644)
Prathmesh Prabhu9995e9b2013-10-31 16:43:55 -0700753
Alex Klein1699fab2022-09-08 08:46:06 -0600754 return sym_file
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400755
Prathmesh Prabhu9995e9b2013-10-31 16:43:55 -0700756
Alex Klein1699fab2022-09-08 08:46:06 -0600757def GenerateBreakpadSymbols(
758 board,
759 breakpad_dir=None,
760 strip_cfi=False,
761 generate_count=None,
762 sysroot=None,
763 num_processes=None,
764 clean_breakpad=False,
765 exclude_dirs=(),
766 file_list=None,
Ian Barkley-Yeungb5274442023-04-28 16:32:20 -0700767 always_use_basic_fallback=False,
768 ignore_expected_files=(),
Alex Klein1699fab2022-09-08 08:46:06 -0600769):
770 """Generate symbols for this board.
Prathmesh Prabhu9995e9b2013-10-31 16:43:55 -0700771
Alex Klein1699fab2022-09-08 08:46:06 -0600772 If |file_list| is None, symbols are generated for all executables, otherwise
773 only for the files included in |file_list|.
Prathmesh Prabhu9995e9b2013-10-31 16:43:55 -0700774
Alex Klein1699fab2022-09-08 08:46:06 -0600775 TODO(build):
776 This should be merged with buildbot_commands.GenerateBreakpadSymbols()
777 once we rewrite cros_generate_breakpad_symbols in python.
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400778
Alex Klein1699fab2022-09-08 08:46:06 -0600779 Args:
Alex Klein8b444532023-04-11 16:35:24 -0600780 board: The board whose symbols we wish to generate
781 breakpad_dir: The full path to the breakpad directory where symbols live
782 strip_cfi: Do not generate CFI data
783 generate_count: If set, only generate this many symbols (meant for
784 testing)
785 sysroot: The root where to find the corresponding ELFs
786 num_processes: Number of jobs to run in parallel
787 clean_breakpad: Should we `rm -rf` the breakpad output dir first; note:
788 we do not do any locking, so do not run more than one in parallel
789 when True
790 exclude_dirs: List of dirs (relative to |sysroot|) to not search
791 file_list: Only generate symbols for files in this list. Each file must
792 be a full path (including |sysroot| prefix).
793 TODO(build): Support paths w/o |sysroot|.
Ian Barkley-Yeungb5274442023-04-28 16:32:20 -0700794 always_use_basic_fallback: If True, use the "basic fallback" mode for
795 all symbol files.
796 ignore_expected_files: A list of ExpectedFiles that will not be
797 considered "missing" if we do not generate symbols for them.
Alex Klein1699fab2022-09-08 08:46:06 -0600798
799 Returns:
Alex Klein8b444532023-04-11 16:35:24 -0600800 The number of errors that were encountered.
Alex Klein1699fab2022-09-08 08:46:06 -0600801 """
802 if sysroot is None:
803 sysroot = build_target_lib.get_default_sysroot_path(board)
804 if breakpad_dir is None:
805 breakpad_dir = FindBreakpadDir(board, sysroot=sysroot)
806 if clean_breakpad:
807 logging.info("cleaning out %s first", breakpad_dir)
808 osutils.RmDir(breakpad_dir, ignore_missing=True, sudo=True)
809 # Make sure non-root can write out symbols as needed.
810 osutils.SafeMakedirs(breakpad_dir, sudo=True)
811 if not os.access(breakpad_dir, os.W_OK):
812 cros_build_lib.sudo_run(["chown", "-R", str(os.getuid()), breakpad_dir])
813 debug_dir = FindDebugDir(board, sysroot=sysroot)
814 exclude_paths = [os.path.join(debug_dir, x) for x in exclude_dirs]
815 if file_list is None:
816 file_list = []
817 file_filter = dict.fromkeys([os.path.normpath(x) for x in file_list], False)
818
819 logging.info("generating breakpad symbols using %s", debug_dir)
820
821 # Let's locate all the debug_files and elfs first along with the debug file
Alex Klein8b444532023-04-11 16:35:24 -0600822 # sizes. This way we can start processing the largest files first in
823 # parallel with the small ones.
Alex Klein1699fab2022-09-08 08:46:06 -0600824 # If |file_list| was given, ignore all other files.
825 targets = []
826 for root, dirs, files in os.walk(debug_dir):
827 if root in exclude_paths:
828 logging.info("Skipping excluded dir %s", root)
829 del dirs[:]
830 continue
831
832 for debug_file in files:
833 debug_file = os.path.join(root, debug_file)
834 # Turn /build/$BOARD/usr/lib/debug/sbin/foo.debug into
835 # /build/$BOARD/sbin/foo.
836 elf_file = os.path.join(
837 sysroot, debug_file[len(debug_dir) + 1 : -6]
838 )
839
840 if file_filter:
841 if elf_file in file_filter:
842 file_filter[elf_file] = True
843 elif debug_file in file_filter:
844 file_filter[debug_file] = True
845 else:
846 continue
847
848 # Filter out files based on common issues with the debug file.
849 if not debug_file.endswith(".debug"):
850 continue
851
852 elif os.path.islink(debug_file):
853 # The build-id stuff is common enough to filter out by default.
854 if "/.build-id/" in debug_file:
855 msg = logging.debug
856 else:
857 msg = logging.warning
858 msg("Skipping symbolic link %s", debug_file)
859 continue
860
861 # Filter out files based on common issues with the elf file.
862 elf_path = os.path.relpath(elf_file, sysroot)
863 debug_only = elf_path in ALLOWED_DEBUG_ONLY_FILES
864 if not os.path.exists(elf_file) and not debug_only:
Alex Klein8b444532023-04-11 16:35:24 -0600865 # Sometimes we filter out programs from /usr/bin but leave
866 # behind the .debug file.
Alex Klein1699fab2022-09-08 08:46:06 -0600867 logging.warning("Skipping missing %s", elf_file)
868 continue
869
870 targets.append((os.path.getsize(debug_file), elf_file, debug_file))
871
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -0800872 with multiprocessing.Manager() as mp_manager:
873 bg_errors = parallel.WrapMultiprocessing(multiprocessing.Value, "i")
874 found_files = parallel.WrapMultiprocessing(mp_manager.list)
875 if file_filter:
876 files_not_found = [
877 x for x, found in file_filter.items() if not found
878 ]
879 bg_errors.value += len(files_not_found)
880 if files_not_found:
881 logging.error(
882 "Failed to find requested files: %s", files_not_found
883 )
Alex Klein1699fab2022-09-08 08:46:06 -0600884
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -0800885 # Now start generating symbols for the discovered elfs.
886 with parallel.BackgroundTaskRunner(
887 GenerateBreakpadSymbol,
888 breakpad_dir=breakpad_dir,
889 strip_cfi=strip_cfi,
890 num_errors=bg_errors,
891 processes=num_processes,
892 sysroot=sysroot,
893 found_files=found_files,
Ian Barkley-Yeungb5274442023-04-28 16:32:20 -0700894 force_basic_fallback=always_use_basic_fallback,
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -0800895 ) as queue:
896 for _, elf_file, debug_file in sorted(targets, reverse=True):
Alex Klein1699fab2022-09-08 08:46:06 -0600897 if generate_count == 0:
898 break
899
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -0800900 queue.put([elf_file, debug_file])
901 if generate_count is not None:
902 generate_count -= 1
903 if generate_count == 0:
904 break
905
Ian Barkley-Yeungb5274442023-04-28 16:32:20 -0700906 missing = (
907 ALL_EXPECTED_FILES
908 - frozenset(found_files)
909 - frozenset(ignore_expected_files)
910 )
911 if (
912 missing
913 and not file_filter
914 and generate_count is None
915 and not always_use_basic_fallback
916 ):
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -0800917 logging.warning(
918 "Not all expected files were processed successfully, "
919 "missing %s",
920 missing,
921 )
922 # TODO(b/270240549): Increment bg_errors.value here once we check
923 # that this isn't going to fail any current builds.
924
Alex Klein1699fab2022-09-08 08:46:06 -0600925 return bg_errors.value
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400926
927
Mike Frysinger3f571af2016-08-31 23:56:53 -0400928def FindDebugDir(board, sysroot=None):
Alex Klein1699fab2022-09-08 08:46:06 -0600929 """Given a |board|, return the path to the split debug dir for it"""
930 if sysroot is None:
931 sysroot = build_target_lib.get_default_sysroot_path(board)
932 return os.path.join(sysroot, "usr", "lib", "debug")
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400933
934
Mike Frysinger3f571af2016-08-31 23:56:53 -0400935def FindBreakpadDir(board, sysroot=None):
Alex Klein1699fab2022-09-08 08:46:06 -0600936 """Given a |board|, return the path to the breakpad dir for it"""
937 return os.path.join(FindDebugDir(board, sysroot=sysroot), "breakpad")
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400938
939
940def main(argv):
Alex Klein1699fab2022-09-08 08:46:06 -0600941 parser = commandline.ArgumentParser(description=__doc__)
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400942
Alex Klein1699fab2022-09-08 08:46:06 -0600943 parser.add_argument(
944 "--board", default=None, help="board to generate symbols for"
945 )
946 parser.add_argument(
947 "--breakpad_root",
948 type="path",
949 default=None,
950 help="root output directory for breakpad symbols",
951 )
952 parser.add_argument(
953 "--sysroot",
954 type="path",
955 default=None,
956 help="root input directory for files",
957 )
958 parser.add_argument(
959 "--exclude-dir",
960 type=str,
961 action="append",
962 default=[],
963 help="directory (relative to |board| root) to not search",
964 )
965 parser.add_argument(
966 "--generate-count",
967 type=int,
968 default=None,
969 help="only generate # number of symbols",
970 )
971 parser.add_argument(
972 "--noclean",
973 dest="clean",
974 action="store_false",
975 default=True,
976 help="do not clean out breakpad dir before running",
977 )
978 parser.add_argument(
979 "--jobs", type=int, default=None, help="limit number of parallel jobs"
980 )
981 parser.add_argument(
982 "--strip_cfi",
983 action="store_true",
984 default=False,
985 help="do not generate CFI data (pass -c to dump_syms)",
986 )
987 parser.add_argument(
Ian Barkley-Yeungb5274442023-04-28 16:32:20 -0700988 "--ignore_errors",
989 action="store_true",
990 default=False,
991 help="Ignore errors from dump_syms, do not validate symbol files, "
992 "just generate symbols best effort",
993 )
994 parser.add_argument(
995 "--ignore_expected_file",
996 type=str,
997 action="append",
998 default=[],
999 choices=[x.name for x in ExpectedFiles],
1000 help="do not generate errors if symbols are not generated for these "
1001 "files",
1002 )
1003 parser.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -06001004 "file_list",
1005 nargs="*",
1006 default=None,
Trent Aptedc4f366a2023-05-16 15:32:48 +10001007 help=(
1008 "generate symbols for only these files "
1009 "(e.g. /build/$BOARD/usr/bin/foo)"
1010 ),
Alex Klein1699fab2022-09-08 08:46:06 -06001011 )
Mike Frysinger69cb41d2013-08-11 20:08:19 -04001012
Alex Klein1699fab2022-09-08 08:46:06 -06001013 opts = parser.parse_args(argv)
1014 opts.Freeze()
Ian Barkley-Yeungb5274442023-04-28 16:32:20 -07001015 ignore_expected_files = [
1016 ExpectedFiles[x] for x in opts.ignore_expected_file
1017 ]
Mike Frysinger69cb41d2013-08-11 20:08:19 -04001018
Alex Klein1699fab2022-09-08 08:46:06 -06001019 if opts.board is None and opts.sysroot is None:
1020 cros_build_lib.Die("--board or --sysroot is required")
Mike Frysinger69cb41d2013-08-11 20:08:19 -04001021
Alex Klein1699fab2022-09-08 08:46:06 -06001022 ret = GenerateBreakpadSymbols(
1023 opts.board,
1024 breakpad_dir=opts.breakpad_root,
1025 strip_cfi=opts.strip_cfi,
1026 generate_count=opts.generate_count,
1027 sysroot=opts.sysroot,
1028 num_processes=opts.jobs,
1029 clean_breakpad=opts.clean,
1030 exclude_dirs=opts.exclude_dir,
1031 file_list=opts.file_list,
Ian Barkley-Yeungb5274442023-04-28 16:32:20 -07001032 always_use_basic_fallback=opts.ignore_errors,
1033 ignore_expected_files=ignore_expected_files,
Alex Klein1699fab2022-09-08 08:46:06 -06001034 )
1035 if ret:
1036 logging.error("encountered %i problem(s)", ret)
Alex Klein8b444532023-04-11 16:35:24 -06001037 # Since exit(status) gets masked, clamp it to 1 so we don't
1038 # inadvertently return 0 in case we are a multiple of the mask.
Alex Klein1699fab2022-09-08 08:46:06 -06001039 ret = 1
Mike Frysinger69cb41d2013-08-11 20:08:19 -04001040
Ian Barkley-Yeungb5274442023-04-28 16:32:20 -07001041 if opts.ignore_errors:
1042 return 0
1043
Alex Klein1699fab2022-09-08 08:46:06 -06001044 return ret