blob: a80128f068bc788914a1b5d87971f548b50c04ef [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 "
258 f"{line_type}"
259 )
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 "
268 f"{expected_words_min} - {expected_words_max})"
269 )
270 elif expected_words_min is not None:
271 if len(words) < expected_words_min:
272 raise ValueError(
273 f"{elf_file}: symbol file has {line_type} line "
274 f"with {len(words)} words (expected "
275 f"{expected_words_min} or more)"
276 )
277
278
279def ValidateSymbolFile(
280 sym_file: str,
281 elf_file: str,
282 sysroot: Optional[str],
283 found_files: Optional[multiprocessing.managers.ListProxy],
284) -> bool:
285 """Checks that the given sym_file has enough info for us to get good stacks.
286
287 Validates that the given sym_file has enough information for us to get
288 good error reports -- enough STACK records to unwind the stack and enough
289 FUNC or PUBLIC records to turn the function addresses into human-readable
290 names.
291
292 Args:
293 sym_file: The complete path to the breakpad symbol file to validate
294 elf_file: The complete path to the elf file which was the source of the
295 symbol file.
296 sysroot: If not None, the root of the build directory ('/build/eve', for
297 instance).
298 found_files: A multiprocessing.managers.ListProxy list containing
299 ExpectedFiles, representing which of the "should always be present"
300 files have been processed.
301
302 Returns:
303 True if the symbol file passes validation.
304 """
305 if sysroot is not None:
306 relative_path = os.path.relpath(elf_file, sysroot)
307 else:
308 relative_path = os.path.relpath(elf_file, "/")
309
310 if relative_path in ALLOWLIST_NO_SYMBOL_FILE_VALIDATION:
311 return True
312 for regex in ALLOWLIST_NO_SYMBOL_FILE_VALIDATION_RE:
313 if regex.match(relative_path):
314 return True
315
316 counts = SymbolFileLineCounts(sym_file, elf_file)
317
318 errors = False
319 if counts.stack_lines == 0:
320 # Use the elf_file in error messages; sym_file is still a temporary
321 # file with a meaningless-to-humans name right now.
322 logging.warning("%s: Symbol file has no STACK records", elf_file)
323 errors = True
324 if counts.module_lines != 1:
325 logging.warning(
326 "%s: Symbol file has %d MODULE lines", elf_file, counts.module_lines
327 )
328 errors = True
329 # Many shared object files have only PUBLIC functions. In theory,
330 # executables should always have at least one FUNC (main) and some line
331 # numbers, but for reasons I'm unclear on, C-based executables often just
332 # have PUBLIC records. dump_syms does not support line numbers after
333 # PUBLIC records, only FUNC records, so such executables will also have
334 # no line numbers.
335 if counts.public_lines == 0 and counts.func_lines == 0:
336 logging.warning(
337 "%s: Symbol file has no FUNC or PUBLIC records", elf_file
338 )
339 errors = True
340 # However, if we get a FUNC record, we do want line numbers for it.
341 if counts.func_lines > 0 and counts.line_number_lines == 0:
342 logging.warning(
343 "%s: Symbol file has FUNC records but no line numbers", elf_file
344 )
345 errors = True
346
347 if counts.line_number_lines > 0 and counts.file_lines == 0:
348 logging.warning(
349 "%s: Symbol file has line number records but no FILE records",
350 elf_file,
351 )
352 errors = True
353 if counts.inline_lines > 0 and counts.file_lines == 0:
354 logging.warning(
355 "%s: Symbol file has INLINE records but no FILE records", elf_file
356 )
357 errors = True
358
359 if counts.inline_lines > 0 and counts.inline_origin_lines == 0:
360 logging.warning(
361 "%s: Symbol file has INLINE records but no INLINE_ORIGIN records",
362 elf_file,
363 )
364 errors = True
365
366 def _AddFoundFile(files, found):
367 """Add another file to the list of expected files we've found."""
368 if files is not None:
369 files.append(found)
370
371 # Extra validation for a few ELF files which are special. Either these are
372 # unusually important to the system (chrome binary, which is where a large
373 # fraction of our crashes occur, and libc.so, which is in every stack), or
374 # they are some hand-chosen ELF files which stand in for "normal" platform2
375 # binaries. Not all ELF files would pass the extra validation, so we can't
376 # run these checks on every ELF, but we want to make sure we don't end up
377 # with, say, a chrome build or a platform2 build with just one or two FUNC
378 # records on every binary.
379 if relative_path == "opt/google/chrome/chrome":
380 _AddFoundFile(found_files, ExpectedFiles.ASH_CHROME)
381 if counts.func_lines < 100000:
382 logging.warning(
383 "chrome should have at least 100,000 FUNC records, found %d",
384 counts.func_lines,
385 )
386 errors = True
387 if counts.stack_lines < 1000000:
388 logging.warning(
389 "chrome should have at least 1,000,000 STACK records, found %d",
390 counts.stack_lines,
391 )
392 errors = True
393 if counts.line_number_lines < 1000000:
394 logging.warning(
395 "chrome should have at least 1,000,000 line number records, "
396 "found %d",
397 counts.line_number_lines,
398 )
399 errors = True
400 # Lacros symbol files are not generated as part of the ChromeOS build and
401 # can't be validated here.
402 # TODO(b/273836486): Add similar logic to the code that generates Lacros
403 # symbols.
Ian Barkley-Yeungab1dab12023-04-21 18:19:55 -0700404 elif LIBC_REGEX.fullmatch(relative_path):
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -0800405 _AddFoundFile(found_files, ExpectedFiles.LIBC)
406 if counts.public_lines < 100:
407 logging.warning(
408 "%s should have at least 100 PUBLIC records, found %d",
409 elf_file,
410 counts.public_lines,
411 )
412 errors = True
413 if counts.stack_lines < 10000:
414 logging.warning(
415 "%s should have at least 10000 STACK records, found %d",
416 elf_file,
417 counts.stack_lines,
418 )
419 errors = True
420 elif relative_path == "sbin/crash_reporter":
421 # Representative platform2 executable.
422 _AddFoundFile(found_files, ExpectedFiles.CRASH_REPORTER)
423 if counts.stack_lines < 1000:
424 logging.warning(
425 "crash_reporter should have at least 1000 STACK records, "
426 "found %d",
427 counts.stack_lines,
428 )
429 errors = True
430 if counts.func_lines < 1000:
431 logging.warning(
432 "crash_reporter should have at least 1000 FUNC records, "
433 "found %d",
434 counts.func_lines,
435 )
436 errors = True
437 if counts.line_number_lines < 10000:
438 logging.warning(
439 "crash_reporter should have at least 10,000 line number "
440 "records, found %d",
441 counts.line_number_lines,
442 )
443 errors = True
444 elif os.path.basename(relative_path) == "libmetrics.so":
445 # Representative platform2 shared library.
446 _AddFoundFile(found_files, ExpectedFiles.LIBMETRICS)
447 if counts.func_lines < 100:
448 logging.warning(
449 "libmetrics should have at least 100 FUNC records, found %d",
450 counts.func_lines,
451 )
452 errors = True
453 if counts.public_lines == 0:
454 logging.warning(
455 "libmetrics should have at least 1 PUBLIC record, found %d",
456 counts.public_lines,
457 )
458 errors = True
459 if counts.stack_lines < 1000:
460 logging.warning(
461 "libmetrics should have at least 1000 STACK records, found %d",
462 counts.stack_lines,
463 )
464 errors = True
465 if counts.line_number_lines < 5000:
466 logging.warning(
467 "libmetrics should have at least 5000 line number records, "
468 "found %d",
469 counts.line_number_lines,
470 )
471 errors = True
472
473 return not errors
474
475
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -0800476def _ExpectGoodSymbols(elf_file, sysroot):
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -0800477 """Determines if we expect dump_syms to create good symbols.
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -0800478
479 We know that certain types of files never generate good symbols. Distinguish
480 those from the majority of elf files which should generate good symbols.
481
482 Args:
483 elf_file: The complete path to the file which we will pass to dump_syms
484 sysroot: If not None, the root of the build directory ('/build/eve', for
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -0800485 instance)
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -0800486
487 Returns:
488 True if the elf file should generate good symbols, False if not.
489 """
490 # .ko files (kernel object files) never produce good symbols.
491 if elf_file.endswith(".ko"):
492 return False
493
494 # dump_syms doesn't understand Golang executables.
495 result = cros_build_lib.run(
496 ["/usr/bin/file", elf_file], print_cmd=False, stdout=True
497 )
498 if b"Go BuildID" in result.stdout:
499 return False
500
501 if sysroot is not None:
502 relative_path = os.path.relpath(elf_file, sysroot)
503 else:
Ian Barkley-Yeunge1785762023-03-08 18:32:18 -0800504 relative_path = os.path.relpath(elf_file, "/")
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -0800505
506 if relative_path in EXPECTED_POOR_SYMBOLIZATION_FILES:
507 return False
508
Ian Barkley-Yeunge1785762023-03-08 18:32:18 -0800509 # Binaries in /usr/local are not actually shipped to end-users, so we
510 # don't care if they get good symbols -- we should never get crash reports
511 # for them anyways.
512 if relative_path.startswith("usr/local"):
513 return False
514
Ian Barkley-Yeung4ed8cee2023-05-05 11:20:52 -0700515 # TODO(b/279668555): tael and tatl libraries in
516 # /opt/google/cros-containers/lib have invalid .debug files (CRC32
517 # mismatches). Since only tael and tatl have
518 # /opt/google/cros-containers/lib, we can just allowlist the directory until
519 # the issue is fixed.
520 if relative_path.startswith("opt/google/cros-containers/lib"):
521 return False
522
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -0800523 return True
524
525
526def ReadSymsHeader(sym_file, name_for_errors):
Alex Klein1699fab2022-09-08 08:46:06 -0600527 """Parse the header of the symbol file
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400528
Alex Klein1699fab2022-09-08 08:46:06 -0600529 The first line of the syms file will read like:
Alex Klein8b444532023-04-11 16:35:24 -0600530 MODULE Linux arm F4F6FA6CCBDEF455039C8DE869C8A2F40 blkid
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400531
Alex Klein1699fab2022-09-08 08:46:06 -0600532 https://code.google.com/p/google-breakpad/wiki/SymbolFiles
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400533
Alex Klein1699fab2022-09-08 08:46:06 -0600534 Args:
Alex Klein8b444532023-04-11 16:35:24 -0600535 sym_file: The symbol file to parse
536 name_for_errors: A name for error strings. Can be the name of the elf
537 file that generated the symbol file, or the name of the symbol file
538 if the symbol file has already been moved to a meaningful location.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500539
Alex Klein1699fab2022-09-08 08:46:06 -0600540 Returns:
Alex Klein8b444532023-04-11 16:35:24 -0600541 A SymbolHeader object
Mike Frysinger1a736a82013-12-12 01:50:59 -0500542
Alex Klein1699fab2022-09-08 08:46:06 -0600543 Raises:
Alex Klein8b444532023-04-11 16:35:24 -0600544 ValueError if the first line of |sym_file| is invalid
Alex Klein1699fab2022-09-08 08:46:06 -0600545 """
546 with file_util.Open(sym_file, "rb") as f:
547 header = f.readline().decode("utf-8").split()
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400548
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -0800549 if len(header) != 5 or header[0] != "MODULE":
550 raise ValueError(
551 f"header of sym file from {name_for_errors} is invalid"
552 )
Mike Frysinger50cedd32014-02-09 23:03:18 -0500553
Alex Klein1699fab2022-09-08 08:46:06 -0600554 return SymbolHeader(
555 os=header[1], cpu=header[2], id=header[3], name=header[4]
556 )
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400557
558
Alex Klein1699fab2022-09-08 08:46:06 -0600559def GenerateBreakpadSymbol(
560 elf_file,
561 debug_file=None,
562 breakpad_dir=None,
563 strip_cfi=False,
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -0800564 sysroot=None,
Alex Klein1699fab2022-09-08 08:46:06 -0600565 num_errors=None,
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -0800566 found_files=None,
Alex Klein1699fab2022-09-08 08:46:06 -0600567 dump_syms_cmd="dump_syms",
Ian Barkley-Yeungb5274442023-04-28 16:32:20 -0700568 force_basic_fallback=False,
Alex Klein1699fab2022-09-08 08:46:06 -0600569):
570 """Generate the symbols for |elf_file| using |debug_file|
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400571
Alex Klein1699fab2022-09-08 08:46:06 -0600572 Args:
Alex Klein8b444532023-04-11 16:35:24 -0600573 elf_file: The file to dump symbols for
574 debug_file: Split debug file to use for symbol information
575 breakpad_dir: The dir to store the output symbol file in
576 strip_cfi: Do not generate CFI data
577 sysroot: Path to the sysroot with the elf_file under it
578 num_errors: An object to update with the error count (needs a .value
579 member).
580 found_files: A multiprocessing.managers.ListProxy list containing
581 ExpectedFiles, representing which of the "should always be present"
582 files have been processed.
583 dump_syms_cmd: Command to use for dumping symbols.
Ian Barkley-Yeungb5274442023-04-28 16:32:20 -0700584 force_basic_fallback: If True, always use _DumpAllowingBasicFallback()
585 instead of _DumpExpectingSymbols().
Mike Frysinger1a736a82013-12-12 01:50:59 -0500586
Alex Klein1699fab2022-09-08 08:46:06 -0600587 Returns:
Alex Klein8b444532023-04-11 16:35:24 -0600588 The name of symbol file written out on success, or the failure count.
Alex Klein1699fab2022-09-08 08:46:06 -0600589 """
590 assert breakpad_dir
591 if num_errors is None:
Trent Apted1e2e4f32023-05-05 03:50:20 +0000592 num_errors = ctypes.c_int(0)
Alex Klein1699fab2022-09-08 08:46:06 -0600593 debug_file_only = not os.path.exists(elf_file)
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400594
Manoj Gupta68546b02023-05-01 21:55:19 +0000595 cmd_base = [dump_syms_cmd, "-v"]
Alex Klein1699fab2022-09-08 08:46:06 -0600596 if strip_cfi:
597 cmd_base += ["-c"]
598 # Some files will not be readable by non-root (e.g. set*id /bin/su).
599 needs_sudo = not os.access(elf_file, os.R_OK)
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400600
Alex Klein1699fab2022-09-08 08:46:06 -0600601 def _DumpIt(cmd_args):
602 if needs_sudo:
603 run_command = cros_build_lib.sudo_run
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400604 else:
Alex Klein1699fab2022-09-08 08:46:06 -0600605 run_command = cros_build_lib.run
606 return run_command(
607 cmd_base + cmd_args,
608 stderr=True,
609 stdout=temp.name,
610 check=False,
611 debug_level=logging.DEBUG,
612 )
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400613
Alex Klein1699fab2022-09-08 08:46:06 -0600614 def _CrashCheck(result, file_or_files, msg):
615 if result.returncode:
616 cbuildbot_alerts.PrintBuildbotStepWarnings()
617 if result.returncode < 0:
618 logging.warning(
619 "dump_syms %s crashed with %s; %s",
620 file_or_files,
621 signals.StrSignal(-result.returncode),
622 msg,
623 )
624 else:
625 logging.warning(
626 "dump_syms %s returned %d; %s",
627 file_or_files,
628 result.returncode,
629 msg,
630 )
631 logging.warning("output:\n%s", result.stderr.decode("utf-8"))
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400632
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -0800633 def _DumpAllowingBasicFallback():
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -0800634 """Dump symbols for an ELF when we do NOT expect to get good symbols.
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -0800635
636 Returns:
637 A SymbolGenerationResult
638 """
Alex Klein1699fab2022-09-08 08:46:06 -0600639 if debug_file:
640 # Try to dump the symbols using the debug file like normal.
641 if debug_file_only:
642 cmd_args = [debug_file]
643 file_or_files = debug_file
644 else:
645 cmd_args = [elf_file, os.path.dirname(debug_file)]
646 file_or_files = [elf_file, debug_file]
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400647
Alex Klein1699fab2022-09-08 08:46:06 -0600648 result = _DumpIt(cmd_args)
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400649
Alex Klein1699fab2022-09-08 08:46:06 -0600650 if result.returncode:
651 # Sometimes dump_syms can crash because there's too much info.
652 # Try dumping and stripping the extended stuff out. At least
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -0800653 # this way we'll get the extended symbols.
654 # https://crbug.com/266064
Alex Klein1699fab2022-09-08 08:46:06 -0600655 _CrashCheck(result, file_or_files, "retrying w/out CFI")
656 cmd_args = ["-c", "-r"] + cmd_args
657 result = _DumpIt(cmd_args)
658 _CrashCheck(result, file_or_files, "retrying w/out debug")
Prathmesh Prabhu9995e9b2013-10-31 16:43:55 -0700659
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -0800660 if not result.returncode:
661 return SymbolGenerationResult.SUCCESS
Prathmesh Prabhu9995e9b2013-10-31 16:43:55 -0700662
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -0800663 # If that didn't work (no debug, or dump_syms still failed), try
664 # dumping just the file itself directly.
665 result = _DumpIt([elf_file])
666 if result.returncode:
667 # A lot of files (like kernel files) contain no debug information,
668 # do not consider such occurrences as errors.
669 cbuildbot_alerts.PrintBuildbotStepWarnings()
670 if b"file contains no debugging information" in result.stderr:
671 logging.warning("dump_syms failed; giving up entirely.")
672 logging.warning("No symbols found for %s", elf_file)
673 return SymbolGenerationResult.EXPECTED_FAILURE
674 else:
675 _CrashCheck(result, elf_file, "counting as failure")
676 return SymbolGenerationResult.UNEXPECTED_FAILURE
677
678 return SymbolGenerationResult.SUCCESS
679
680 def _DumpExpectingSymbols():
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -0800681 """Dump symbols for an ELF when we expect to get good symbols.
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -0800682
683 Returns:
684 A SymbolGenerationResult. We never expect failure, so the result
685 will always be SUCCESS or UNEXPECTED_FAILURE.
686 """
687 if not debug_file:
688 logging.warning("%s must have debug file", elf_file)
689 return SymbolGenerationResult.UNEXPECTED_FAILURE
690
691 cmd_args = [elf_file, os.path.dirname(debug_file)]
692 result = _DumpIt(cmd_args)
693 if result.returncode:
Ian Barkley-Yeungab1dab12023-04-21 18:19:55 -0700694 _CrashCheck(
695 result,
696 [elf_file, debug_file],
697 "unexpected symbol generation failure",
698 )
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -0800699 return SymbolGenerationResult.UNEXPECTED_FAILURE
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -0800700
701 # TODO(b/270240549): Remove try/except, allow exceptions to just
702 # fail the script. The try/except is just here until we are sure this
703 # will not break the build.
704 try:
705 if not ValidateSymbolFile(
706 temp.name, elf_file, sysroot, found_files
707 ):
708 logging.warning("%s: symbol file failed validation", elf_file)
709 return SymbolGenerationResult.UNEXPECTED_FAILURE
710 except ValueError as e:
711 logging.warning(
712 "%s: symbol file failed validation due to exception %s",
713 elf_file,
714 e,
715 )
716 return SymbolGenerationResult.UNEXPECTED_FAILURE
717
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -0800718 return SymbolGenerationResult.SUCCESS
719
720 osutils.SafeMakedirs(breakpad_dir)
721 with cros_build_lib.UnbufferedNamedTemporaryFile(
722 dir=breakpad_dir, delete=False
723 ) as temp:
Ian Barkley-Yeungb5274442023-04-28 16:32:20 -0700724 if not force_basic_fallback and _ExpectGoodSymbols(elf_file, sysroot):
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -0800725 result = _DumpExpectingSymbols()
726 # Until the EXPECTED_POOR_SYMBOLIZATION_FILES allowlist is
727 # completely set up for all boards, don't fail the build if
728 # _ExpectGoodSymbols is wrong.
729 # TODO(b/241470012): Remove the call to _DumpAllowingBasicFallback()
730 # and just error out if _DumpExpectingSymbols fails.
731 if result == SymbolGenerationResult.UNEXPECTED_FAILURE:
732 result = _DumpAllowingBasicFallback()
733 else:
734 result = _DumpAllowingBasicFallback()
735
736 if result == SymbolGenerationResult.UNEXPECTED_FAILURE:
737 num_errors.value += 1
738 os.unlink(temp.name)
739 return num_errors.value
740
741 if result == SymbolGenerationResult.EXPECTED_FAILURE:
742 os.unlink(temp.name)
743 return 0
Prathmesh Prabhu9995e9b2013-10-31 16:43:55 -0700744
Alex Klein1699fab2022-09-08 08:46:06 -0600745 # Move the dumped symbol file to the right place:
Alex Klein8b444532023-04-11 16:35:24 -0600746 # /$SYSROOT/usr/lib/debug/breakpad/<module-name>/<id>/<module-name>.sym
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -0800747 header = ReadSymsHeader(temp, elf_file)
Alex Klein1699fab2022-09-08 08:46:06 -0600748 logging.info("Dumped %s as %s : %s", elf_file, header.name, header.id)
749 sym_file = os.path.join(
750 breakpad_dir, header.name, header.id, header.name + ".sym"
751 )
752 osutils.SafeMakedirs(os.path.dirname(sym_file))
753 os.rename(temp.name, sym_file)
754 os.chmod(sym_file, 0o644)
Prathmesh Prabhu9995e9b2013-10-31 16:43:55 -0700755
Alex Klein1699fab2022-09-08 08:46:06 -0600756 return sym_file
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400757
Prathmesh Prabhu9995e9b2013-10-31 16:43:55 -0700758
Alex Klein1699fab2022-09-08 08:46:06 -0600759def GenerateBreakpadSymbols(
760 board,
761 breakpad_dir=None,
762 strip_cfi=False,
763 generate_count=None,
764 sysroot=None,
765 num_processes=None,
766 clean_breakpad=False,
767 exclude_dirs=(),
768 file_list=None,
Ian Barkley-Yeungb5274442023-04-28 16:32:20 -0700769 always_use_basic_fallback=False,
770 ignore_expected_files=(),
Alex Klein1699fab2022-09-08 08:46:06 -0600771):
772 """Generate symbols for this board.
Prathmesh Prabhu9995e9b2013-10-31 16:43:55 -0700773
Alex Klein1699fab2022-09-08 08:46:06 -0600774 If |file_list| is None, symbols are generated for all executables, otherwise
775 only for the files included in |file_list|.
Prathmesh Prabhu9995e9b2013-10-31 16:43:55 -0700776
Alex Klein1699fab2022-09-08 08:46:06 -0600777 TODO(build):
778 This should be merged with buildbot_commands.GenerateBreakpadSymbols()
779 once we rewrite cros_generate_breakpad_symbols in python.
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400780
Alex Klein1699fab2022-09-08 08:46:06 -0600781 Args:
Alex Klein8b444532023-04-11 16:35:24 -0600782 board: The board whose symbols we wish to generate
783 breakpad_dir: The full path to the breakpad directory where symbols live
784 strip_cfi: Do not generate CFI data
785 generate_count: If set, only generate this many symbols (meant for
786 testing)
787 sysroot: The root where to find the corresponding ELFs
788 num_processes: Number of jobs to run in parallel
789 clean_breakpad: Should we `rm -rf` the breakpad output dir first; note:
790 we do not do any locking, so do not run more than one in parallel
791 when True
792 exclude_dirs: List of dirs (relative to |sysroot|) to not search
793 file_list: Only generate symbols for files in this list. Each file must
794 be a full path (including |sysroot| prefix).
795 TODO(build): Support paths w/o |sysroot|.
Ian Barkley-Yeungb5274442023-04-28 16:32:20 -0700796 always_use_basic_fallback: If True, use the "basic fallback" mode for
797 all symbol files.
798 ignore_expected_files: A list of ExpectedFiles that will not be
799 considered "missing" if we do not generate symbols for them.
Alex Klein1699fab2022-09-08 08:46:06 -0600800
801 Returns:
Alex Klein8b444532023-04-11 16:35:24 -0600802 The number of errors that were encountered.
Alex Klein1699fab2022-09-08 08:46:06 -0600803 """
804 if sysroot is None:
805 sysroot = build_target_lib.get_default_sysroot_path(board)
806 if breakpad_dir is None:
807 breakpad_dir = FindBreakpadDir(board, sysroot=sysroot)
808 if clean_breakpad:
809 logging.info("cleaning out %s first", breakpad_dir)
810 osutils.RmDir(breakpad_dir, ignore_missing=True, sudo=True)
811 # Make sure non-root can write out symbols as needed.
812 osutils.SafeMakedirs(breakpad_dir, sudo=True)
813 if not os.access(breakpad_dir, os.W_OK):
814 cros_build_lib.sudo_run(["chown", "-R", str(os.getuid()), breakpad_dir])
815 debug_dir = FindDebugDir(board, sysroot=sysroot)
816 exclude_paths = [os.path.join(debug_dir, x) for x in exclude_dirs]
817 if file_list is None:
818 file_list = []
819 file_filter = dict.fromkeys([os.path.normpath(x) for x in file_list], False)
820
821 logging.info("generating breakpad symbols using %s", debug_dir)
822
823 # Let's locate all the debug_files and elfs first along with the debug file
Alex Klein8b444532023-04-11 16:35:24 -0600824 # sizes. This way we can start processing the largest files first in
825 # parallel with the small ones.
Alex Klein1699fab2022-09-08 08:46:06 -0600826 # If |file_list| was given, ignore all other files.
827 targets = []
828 for root, dirs, files in os.walk(debug_dir):
829 if root in exclude_paths:
830 logging.info("Skipping excluded dir %s", root)
831 del dirs[:]
832 continue
833
834 for debug_file in files:
835 debug_file = os.path.join(root, debug_file)
836 # Turn /build/$BOARD/usr/lib/debug/sbin/foo.debug into
837 # /build/$BOARD/sbin/foo.
838 elf_file = os.path.join(
839 sysroot, debug_file[len(debug_dir) + 1 : -6]
840 )
841
842 if file_filter:
843 if elf_file in file_filter:
844 file_filter[elf_file] = True
845 elif debug_file in file_filter:
846 file_filter[debug_file] = True
847 else:
848 continue
849
850 # Filter out files based on common issues with the debug file.
851 if not debug_file.endswith(".debug"):
852 continue
853
854 elif os.path.islink(debug_file):
855 # The build-id stuff is common enough to filter out by default.
856 if "/.build-id/" in debug_file:
857 msg = logging.debug
858 else:
859 msg = logging.warning
860 msg("Skipping symbolic link %s", debug_file)
861 continue
862
863 # Filter out files based on common issues with the elf file.
864 elf_path = os.path.relpath(elf_file, sysroot)
865 debug_only = elf_path in ALLOWED_DEBUG_ONLY_FILES
866 if not os.path.exists(elf_file) and not debug_only:
Alex Klein8b444532023-04-11 16:35:24 -0600867 # Sometimes we filter out programs from /usr/bin but leave
868 # behind the .debug file.
Alex Klein1699fab2022-09-08 08:46:06 -0600869 logging.warning("Skipping missing %s", elf_file)
870 continue
871
872 targets.append((os.path.getsize(debug_file), elf_file, debug_file))
873
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -0800874 with multiprocessing.Manager() as mp_manager:
875 bg_errors = parallel.WrapMultiprocessing(multiprocessing.Value, "i")
876 found_files = parallel.WrapMultiprocessing(mp_manager.list)
877 if file_filter:
878 files_not_found = [
879 x for x, found in file_filter.items() if not found
880 ]
881 bg_errors.value += len(files_not_found)
882 if files_not_found:
883 logging.error(
884 "Failed to find requested files: %s", files_not_found
885 )
Alex Klein1699fab2022-09-08 08:46:06 -0600886
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -0800887 # Now start generating symbols for the discovered elfs.
888 with parallel.BackgroundTaskRunner(
889 GenerateBreakpadSymbol,
890 breakpad_dir=breakpad_dir,
891 strip_cfi=strip_cfi,
892 num_errors=bg_errors,
893 processes=num_processes,
894 sysroot=sysroot,
895 found_files=found_files,
Ian Barkley-Yeungb5274442023-04-28 16:32:20 -0700896 force_basic_fallback=always_use_basic_fallback,
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -0800897 ) as queue:
898 for _, elf_file, debug_file in sorted(targets, reverse=True):
Alex Klein1699fab2022-09-08 08:46:06 -0600899 if generate_count == 0:
900 break
901
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -0800902 queue.put([elf_file, debug_file])
903 if generate_count is not None:
904 generate_count -= 1
905 if generate_count == 0:
906 break
907
Ian Barkley-Yeungb5274442023-04-28 16:32:20 -0700908 missing = (
909 ALL_EXPECTED_FILES
910 - frozenset(found_files)
911 - frozenset(ignore_expected_files)
912 )
913 if (
914 missing
915 and not file_filter
916 and generate_count is None
917 and not always_use_basic_fallback
918 ):
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -0800919 logging.warning(
920 "Not all expected files were processed successfully, "
921 "missing %s",
922 missing,
923 )
924 # TODO(b/270240549): Increment bg_errors.value here once we check
925 # that this isn't going to fail any current builds.
926
Alex Klein1699fab2022-09-08 08:46:06 -0600927 return bg_errors.value
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400928
929
Mike Frysinger3f571af2016-08-31 23:56:53 -0400930def FindDebugDir(board, sysroot=None):
Alex Klein1699fab2022-09-08 08:46:06 -0600931 """Given a |board|, return the path to the split debug dir for it"""
932 if sysroot is None:
933 sysroot = build_target_lib.get_default_sysroot_path(board)
934 return os.path.join(sysroot, "usr", "lib", "debug")
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400935
936
Mike Frysinger3f571af2016-08-31 23:56:53 -0400937def FindBreakpadDir(board, sysroot=None):
Alex Klein1699fab2022-09-08 08:46:06 -0600938 """Given a |board|, return the path to the breakpad dir for it"""
939 return os.path.join(FindDebugDir(board, sysroot=sysroot), "breakpad")
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400940
941
942def main(argv):
Alex Klein1699fab2022-09-08 08:46:06 -0600943 parser = commandline.ArgumentParser(description=__doc__)
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400944
Alex Klein1699fab2022-09-08 08:46:06 -0600945 parser.add_argument(
946 "--board", default=None, help="board to generate symbols for"
947 )
948 parser.add_argument(
949 "--breakpad_root",
950 type="path",
951 default=None,
952 help="root output directory for breakpad symbols",
953 )
954 parser.add_argument(
955 "--sysroot",
956 type="path",
957 default=None,
958 help="root input directory for files",
959 )
960 parser.add_argument(
961 "--exclude-dir",
962 type=str,
963 action="append",
964 default=[],
965 help="directory (relative to |board| root) to not search",
966 )
967 parser.add_argument(
968 "--generate-count",
969 type=int,
970 default=None,
971 help="only generate # number of symbols",
972 )
973 parser.add_argument(
974 "--noclean",
975 dest="clean",
976 action="store_false",
977 default=True,
978 help="do not clean out breakpad dir before running",
979 )
980 parser.add_argument(
981 "--jobs", type=int, default=None, help="limit number of parallel jobs"
982 )
983 parser.add_argument(
984 "--strip_cfi",
985 action="store_true",
986 default=False,
987 help="do not generate CFI data (pass -c to dump_syms)",
988 )
989 parser.add_argument(
Ian Barkley-Yeungb5274442023-04-28 16:32:20 -0700990 "--ignore_errors",
991 action="store_true",
992 default=False,
993 help="Ignore errors from dump_syms, do not validate symbol files, "
994 "just generate symbols best effort",
995 )
996 parser.add_argument(
997 "--ignore_expected_file",
998 type=str,
999 action="append",
1000 default=[],
1001 choices=[x.name for x in ExpectedFiles],
1002 help="do not generate errors if symbols are not generated for these "
1003 "files",
1004 )
1005 parser.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -06001006 "file_list",
1007 nargs="*",
1008 default=None,
Trent Aptedc4f366a2023-05-16 15:32:48 +10001009 help=(
1010 "generate symbols for only these files "
1011 "(e.g. /build/$BOARD/usr/bin/foo)"
1012 ),
Alex Klein1699fab2022-09-08 08:46:06 -06001013 )
Mike Frysinger69cb41d2013-08-11 20:08:19 -04001014
Alex Klein1699fab2022-09-08 08:46:06 -06001015 opts = parser.parse_args(argv)
1016 opts.Freeze()
Ian Barkley-Yeungb5274442023-04-28 16:32:20 -07001017 ignore_expected_files = [
1018 ExpectedFiles[x] for x in opts.ignore_expected_file
1019 ]
Mike Frysinger69cb41d2013-08-11 20:08:19 -04001020
Alex Klein1699fab2022-09-08 08:46:06 -06001021 if opts.board is None and opts.sysroot is None:
1022 cros_build_lib.Die("--board or --sysroot is required")
Mike Frysinger69cb41d2013-08-11 20:08:19 -04001023
Alex Klein1699fab2022-09-08 08:46:06 -06001024 ret = GenerateBreakpadSymbols(
1025 opts.board,
1026 breakpad_dir=opts.breakpad_root,
1027 strip_cfi=opts.strip_cfi,
1028 generate_count=opts.generate_count,
1029 sysroot=opts.sysroot,
1030 num_processes=opts.jobs,
1031 clean_breakpad=opts.clean,
1032 exclude_dirs=opts.exclude_dir,
1033 file_list=opts.file_list,
Ian Barkley-Yeungb5274442023-04-28 16:32:20 -07001034 always_use_basic_fallback=opts.ignore_errors,
1035 ignore_expected_files=ignore_expected_files,
Alex Klein1699fab2022-09-08 08:46:06 -06001036 )
1037 if ret:
1038 logging.error("encountered %i problem(s)", ret)
Alex Klein8b444532023-04-11 16:35:24 -06001039 # Since exit(status) gets masked, clamp it to 1 so we don't
1040 # inadvertently return 0 in case we are a multiple of the mask.
Alex Klein1699fab2022-09-08 08:46:06 -06001041 ret = 1
Mike Frysinger69cb41d2013-08-11 20:08:19 -04001042
Ian Barkley-Yeungb5274442023-04-28 16:32:20 -07001043 if opts.ignore_errors:
1044 return 0
1045
Alex Klein1699fab2022-09-08 08:46:06 -06001046 return ret