blob: eaf630f6e2cef02141ecebc999c3ce668537858f [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",
Ian Barkley-Yeungab1dab12023-04-21 18:19:55 -070069 # TODO(b/279645511): Investigate why this doesn't have STACK records on
70 # jacuzzi, scarlet, kukui, etc.
71 "usr/bin/rma_reset",
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -080072}
73# Same but patterns not exact paths.
74ALLOWLIST_NO_SYMBOL_FILE_VALIDATION_RE = tuple(
75 re.compile(x)
76 for x in (
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -080077 # Prebuilt closed-source library.
78 r"usr/lib[^/]*/python[0-9]\.[0-9]/site-packages/.*/x_ignore_nofocus.so",
79 # b/273577373: Rarely used, and only by few programs that do
Ian Barkley-Yeung5b485132023-07-25 16:54:31 -070080 # non-standard encoding conversions.
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -080081 r"lib[^/]*/libnss_files\.so\.[0-9.]+",
82 r"lib[^/]*/libnss_dns\.so\.[0-9.]+",
83 r"usr/lib[^/]*/gconv/libISOIR165\.so",
84 r"usr/lib[^/]*/gconv/libGB\.so",
85 r"usr/lib[^/]*/gconv/libKSC\.so",
86 r"usr/lib[^/]*/gconv/libCNS\.so",
87 r"usr/lib[^/]*/gconv/libJISX0213\.so",
88 r"usr/lib[^/]*/gconv/libJIS\.so",
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -080089 )
90)
91
Alex Klein1699fab2022-09-08 08:46:06 -060092SymbolHeader = collections.namedtuple(
93 "SymbolHeader",
94 (
95 "cpu",
96 "id",
97 "name",
98 "os",
99 ),
100)
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400101
102
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -0800103class SymbolGenerationResult(enum.Enum):
104 """Result of running dump_syms
105
106 Return value of _DumpAllowingBasicFallback() and _DumpExpectingSymbols().
107 """
108
109 SUCCESS = 1
110 UNEXPECTED_FAILURE = 2
111 EXPECTED_FAILURE = 3
112
113
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -0800114class ExpectedFiles(enum.Enum):
115 """The files always expect to see dump_syms run on.
116
117 We do extra validation on a few, semi-randomly chosen files. If we do not
118 create symbol files for these ELFs, something is very wrong.
119 """
120
121 ASH_CHROME = enum.auto()
122 LIBC = enum.auto()
123 CRASH_REPORTER = enum.auto()
124 LIBMETRICS = enum.auto()
125
126
127ALL_EXPECTED_FILES = frozenset(
128 (
129 ExpectedFiles.ASH_CHROME,
130 ExpectedFiles.LIBC,
131 ExpectedFiles.CRASH_REPORTER,
132 ExpectedFiles.LIBMETRICS,
133 )
134)
135
136
Ian Barkley-Yeungab1dab12023-04-21 18:19:55 -0700137# Regular expression for ChromeOS's libc.so. Note that some containers have
138# their own libc.so file; we don't want to do the extra validation on those.
139# (They are often subsets of the full libc and will not pass STACK count tests.)
140LIBC_REGEX = re.compile(r"lib[^/]*/libc\.so\.[0-9.]+")
141
Ian Barkley-Yeung5b485132023-07-25 16:54:31 -0700142# Regular expression to find shared object libraries. Covers filenames like
143# "libcontainer.so" and also "libc.so.6" and also
144# "libabsl_log_entry.so.2301.0.0".
145SO_REGEX = re.compile(r"\.so(?:\.[0-9]+)*$")
146
147
148def IsSharedLibrary(elf_file: str) -> Optional[re.Match]:
149 """Returns non-None if the elf_file appears to be a shared library.
150
151 Tests if the elf_file appears to be a shared object library. The test is
152 just based on the filename.
153
154 Args:
155 elf_file: The path of the elf_file being tested.
156
157 Returns:
158 An object that evaluates to true if the elf_file is a shared object
159 library. None if the elf_file is not a shared object library.
160 """
161 return SO_REGEX.search(elf_file)
162
Ian Barkley-Yeungab1dab12023-04-21 18:19:55 -0700163
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -0800164class SymbolFileLineCounts:
165 """Counts of the various types of lines in a .sym file"""
166
167 LINE_NUMBER_REGEX = re.compile(r"^([0-9a-f]+)")
168
169 def __init__(self, sym_file: str, elf_file: str):
170 # https://chromium.googlesource.com/breakpad/breakpad/+/HEAD/docs/symbol_files.md
171 # explains what these line types are.
172 self.module_lines = 0
173 self.file_lines = 0
174 self.inline_origin_lines = 0
175 self.func_lines = 0
176 self.inline_lines = 0
177 self.line_number_lines = 0
178 self.public_lines = 0
179 self.stack_lines = 0
180 # Not listed in the documentation but still present.
181 self.info_lines = 0
182
183 with open(sym_file, mode="r", encoding="utf-8") as f:
184 for line in f:
185 words = line.split()
186 expected_words_max = None
187 if not words:
188 raise ValueError(
189 f"{elf_file}: symbol file has unexpected blank line"
190 )
191
192 line_type = words[0]
193 if line_type == "MODULE":
194 self.module_lines += 1
195 expected_words_min = 5
196 expected_words_max = 5
197 elif line_type == "FILE":
198 self.file_lines += 1
199 expected_words_min = 3
200 # No max, filenames can have spaces.
201 elif line_type == "INLINE_ORIGIN":
202 self.inline_origin_lines += 1
203 expected_words_min = 3
204 # No max, function parameter lists can have spaces.
205 elif line_type == "FUNC":
206 self.func_lines += 1
207 expected_words_min = 5
208 # No max, function parameter lists can have spaces.
209 elif line_type == "INLINE":
210 self.inline_lines += 1
211 expected_words_min = 5
212 # No max, INLINE can have multiple address pairs.
213 elif SymbolFileLineCounts.LINE_NUMBER_REGEX.match(line_type):
214 self.line_number_lines += 1
215 expected_words_min = 4
216 expected_words_max = 4
217 line_type = "line number"
218 elif line_type == "PUBLIC":
219 self.public_lines += 1
Ian Barkley-Yeungab1dab12023-04-21 18:19:55 -0700220 # TODO(b/251003272): expected_words_min should be 4;
221 # however, dump_syms sometimes produces PUBLIC records with
222 # no symbol name. This is an error but is not affecting our
223 # ability to decode stacks.
224 expected_words_min = 3
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -0800225 # No max, function parameter lists can have spaces.
226 elif line_type == "STACK":
227 self.stack_lines += 1
228 expected_words_min = 5
229 # No max, expressions can be complex.
230 elif line_type == "INFO":
231 self.info_lines += 1
232 # Not documented, so unclear what the min & max are
233 expected_words_min = None
234 else:
235 raise ValueError(
236 f"{elf_file}: symbol file has unknown line type "
Ian Barkley-Yeungccfa1b52023-07-07 18:55:46 -0700237 f"{line_type} (line='{line}')"
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -0800238 )
239
240 if expected_words_max is not None:
241 if not (
242 expected_words_min <= len(words) <= expected_words_max
243 ):
244 raise ValueError(
245 f"{elf_file}: symbol file has {line_type} line "
246 f"with {len(words)} words (expected "
Ian Barkley-Yeungccfa1b52023-07-07 18:55:46 -0700247 f"{expected_words_min} - {expected_words_max}) "
248 f"(line='{line}')"
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -0800249 )
250 elif expected_words_min is not None:
251 if len(words) < expected_words_min:
252 raise ValueError(
253 f"{elf_file}: symbol file has {line_type} line "
254 f"with {len(words)} words (expected "
Ian Barkley-Yeungccfa1b52023-07-07 18:55:46 -0700255 f"{expected_words_min} or more) (line='{line}')"
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -0800256 )
257
258
259def ValidateSymbolFile(
260 sym_file: str,
261 elf_file: str,
262 sysroot: Optional[str],
263 found_files: Optional[multiprocessing.managers.ListProxy],
264) -> bool:
265 """Checks that the given sym_file has enough info for us to get good stacks.
266
267 Validates that the given sym_file has enough information for us to get
268 good error reports -- enough STACK records to unwind the stack and enough
269 FUNC or PUBLIC records to turn the function addresses into human-readable
270 names.
271
272 Args:
273 sym_file: The complete path to the breakpad symbol file to validate
274 elf_file: The complete path to the elf file which was the source of the
275 symbol file.
276 sysroot: If not None, the root of the build directory ('/build/eve', for
277 instance).
278 found_files: A multiprocessing.managers.ListProxy list containing
279 ExpectedFiles, representing which of the "should always be present"
280 files have been processed.
281
282 Returns:
283 True if the symbol file passes validation.
284 """
285 if sysroot is not None:
286 relative_path = os.path.relpath(elf_file, sysroot)
287 else:
288 relative_path = os.path.relpath(elf_file, "/")
289
290 if relative_path in ALLOWLIST_NO_SYMBOL_FILE_VALIDATION:
291 return True
292 for regex in ALLOWLIST_NO_SYMBOL_FILE_VALIDATION_RE:
293 if regex.match(relative_path):
294 return True
295
296 counts = SymbolFileLineCounts(sym_file, elf_file)
297
298 errors = False
Ian Barkley-Yeung5b485132023-07-25 16:54:31 -0700299 # Executables should always have code, and thus STACK records. Many shared
300 # libraries, however, just have some constants (libabsl_log_entry.so) or
301 # are stubs that only have code under some #if or USE condition
302 # (libcros_ml_core.so). It is correct for such shared libraries to have no
303 # STACK records.
304 is_shared_library = IsSharedLibrary(elf_file)
305 if counts.stack_lines == 0 and not is_shared_library:
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -0800306 # Use the elf_file in error messages; sym_file is still a temporary
307 # file with a meaningless-to-humans name right now.
308 logging.warning("%s: Symbol file has no STACK records", elf_file)
309 errors = True
310 if counts.module_lines != 1:
311 logging.warning(
312 "%s: Symbol file has %d MODULE lines", elf_file, counts.module_lines
313 )
314 errors = True
315 # Many shared object files have only PUBLIC functions. In theory,
316 # executables should always have at least one FUNC (main) and some line
317 # numbers, but for reasons I'm unclear on, C-based executables often just
318 # have PUBLIC records. dump_syms does not support line numbers after
319 # PUBLIC records, only FUNC records, so such executables will also have
320 # no line numbers.
321 if counts.public_lines == 0 and counts.func_lines == 0:
322 logging.warning(
323 "%s: Symbol file has no FUNC or PUBLIC records", elf_file
324 )
325 errors = True
326 # However, if we get a FUNC record, we do want line numbers for it.
327 if counts.func_lines > 0 and counts.line_number_lines == 0:
328 logging.warning(
329 "%s: Symbol file has FUNC records but no line numbers", elf_file
330 )
331 errors = True
332
333 if counts.line_number_lines > 0 and counts.file_lines == 0:
334 logging.warning(
335 "%s: Symbol file has line number records but no FILE records",
336 elf_file,
337 )
338 errors = True
339 if counts.inline_lines > 0 and counts.file_lines == 0:
340 logging.warning(
341 "%s: Symbol file has INLINE records but no FILE records", elf_file
342 )
343 errors = True
344
345 if counts.inline_lines > 0 and counts.inline_origin_lines == 0:
346 logging.warning(
347 "%s: Symbol file has INLINE records but no INLINE_ORIGIN records",
348 elf_file,
349 )
350 errors = True
351
352 def _AddFoundFile(files, found):
353 """Add another file to the list of expected files we've found."""
354 if files is not None:
355 files.append(found)
356
357 # Extra validation for a few ELF files which are special. Either these are
358 # unusually important to the system (chrome binary, which is where a large
359 # fraction of our crashes occur, and libc.so, which is in every stack), or
360 # they are some hand-chosen ELF files which stand in for "normal" platform2
361 # binaries. Not all ELF files would pass the extra validation, so we can't
362 # run these checks on every ELF, but we want to make sure we don't end up
363 # with, say, a chrome build or a platform2 build with just one or two FUNC
364 # records on every binary.
365 if relative_path == "opt/google/chrome/chrome":
366 _AddFoundFile(found_files, ExpectedFiles.ASH_CHROME)
367 if counts.func_lines < 100000:
368 logging.warning(
369 "chrome should have at least 100,000 FUNC records, found %d",
370 counts.func_lines,
371 )
372 errors = True
373 if counts.stack_lines < 1000000:
374 logging.warning(
375 "chrome should have at least 1,000,000 STACK records, found %d",
376 counts.stack_lines,
377 )
378 errors = True
379 if counts.line_number_lines < 1000000:
380 logging.warning(
381 "chrome should have at least 1,000,000 line number records, "
382 "found %d",
383 counts.line_number_lines,
384 )
385 errors = True
386 # Lacros symbol files are not generated as part of the ChromeOS build and
387 # can't be validated here.
388 # TODO(b/273836486): Add similar logic to the code that generates Lacros
389 # symbols.
Ian Barkley-Yeungab1dab12023-04-21 18:19:55 -0700390 elif LIBC_REGEX.fullmatch(relative_path):
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -0800391 _AddFoundFile(found_files, ExpectedFiles.LIBC)
392 if counts.public_lines < 100:
393 logging.warning(
394 "%s should have at least 100 PUBLIC records, found %d",
395 elf_file,
396 counts.public_lines,
397 )
398 errors = True
399 if counts.stack_lines < 10000:
400 logging.warning(
401 "%s should have at least 10000 STACK records, found %d",
402 elf_file,
403 counts.stack_lines,
404 )
405 errors = True
406 elif relative_path == "sbin/crash_reporter":
407 # Representative platform2 executable.
408 _AddFoundFile(found_files, ExpectedFiles.CRASH_REPORTER)
409 if counts.stack_lines < 1000:
410 logging.warning(
411 "crash_reporter should have at least 1000 STACK records, "
412 "found %d",
413 counts.stack_lines,
414 )
415 errors = True
416 if counts.func_lines < 1000:
417 logging.warning(
418 "crash_reporter should have at least 1000 FUNC records, "
419 "found %d",
420 counts.func_lines,
421 )
422 errors = True
423 if counts.line_number_lines < 10000:
424 logging.warning(
425 "crash_reporter should have at least 10,000 line number "
426 "records, found %d",
427 counts.line_number_lines,
428 )
429 errors = True
430 elif os.path.basename(relative_path) == "libmetrics.so":
431 # Representative platform2 shared library.
432 _AddFoundFile(found_files, ExpectedFiles.LIBMETRICS)
433 if counts.func_lines < 100:
434 logging.warning(
435 "libmetrics should have at least 100 FUNC records, found %d",
436 counts.func_lines,
437 )
438 errors = True
439 if counts.public_lines == 0:
440 logging.warning(
441 "libmetrics should have at least 1 PUBLIC record, found %d",
442 counts.public_lines,
443 )
444 errors = True
445 if counts.stack_lines < 1000:
446 logging.warning(
447 "libmetrics should have at least 1000 STACK records, found %d",
448 counts.stack_lines,
449 )
450 errors = True
451 if counts.line_number_lines < 5000:
452 logging.warning(
453 "libmetrics should have at least 5000 line number records, "
454 "found %d",
455 counts.line_number_lines,
456 )
457 errors = True
458
459 return not errors
460
461
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -0800462def _ExpectGoodSymbols(elf_file, sysroot):
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -0800463 """Determines if we expect dump_syms to create good symbols.
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -0800464
465 We know that certain types of files never generate good symbols. Distinguish
466 those from the majority of elf files which should generate good symbols.
467
468 Args:
469 elf_file: The complete path to the file which we will pass to dump_syms
470 sysroot: If not None, the root of the build directory ('/build/eve', for
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -0800471 instance)
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -0800472
473 Returns:
474 True if the elf file should generate good symbols, False if not.
475 """
476 # .ko files (kernel object files) never produce good symbols.
477 if elf_file.endswith(".ko"):
478 return False
479
480 # dump_syms doesn't understand Golang executables.
481 result = cros_build_lib.run(
482 ["/usr/bin/file", elf_file], print_cmd=False, stdout=True
483 )
484 if b"Go BuildID" in result.stdout:
485 return False
486
487 if sysroot is not None:
488 relative_path = os.path.relpath(elf_file, sysroot)
489 else:
Ian Barkley-Yeunge1785762023-03-08 18:32:18 -0800490 relative_path = os.path.relpath(elf_file, "/")
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -0800491
492 if relative_path in EXPECTED_POOR_SYMBOLIZATION_FILES:
493 return False
494
Ian Barkley-Yeunge1785762023-03-08 18:32:18 -0800495 # Binaries in /usr/local are not actually shipped to end-users, so we
496 # don't care if they get good symbols -- we should never get crash reports
497 # for them anyways.
498 if relative_path.startswith("usr/local"):
499 return False
500
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -0800501 return True
502
503
504def ReadSymsHeader(sym_file, name_for_errors):
Alex Klein1699fab2022-09-08 08:46:06 -0600505 """Parse the header of the symbol file
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400506
Alex Klein1699fab2022-09-08 08:46:06 -0600507 The first line of the syms file will read like:
Alex Klein8b444532023-04-11 16:35:24 -0600508 MODULE Linux arm F4F6FA6CCBDEF455039C8DE869C8A2F40 blkid
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400509
Alex Klein1699fab2022-09-08 08:46:06 -0600510 https://code.google.com/p/google-breakpad/wiki/SymbolFiles
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400511
Alex Klein1699fab2022-09-08 08:46:06 -0600512 Args:
Alex Klein8b444532023-04-11 16:35:24 -0600513 sym_file: The symbol file to parse
514 name_for_errors: A name for error strings. Can be the name of the elf
515 file that generated the symbol file, or the name of the symbol file
516 if the symbol file has already been moved to a meaningful location.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500517
Alex Klein1699fab2022-09-08 08:46:06 -0600518 Returns:
Alex Klein8b444532023-04-11 16:35:24 -0600519 A SymbolHeader object
Mike Frysinger1a736a82013-12-12 01:50:59 -0500520
Alex Klein1699fab2022-09-08 08:46:06 -0600521 Raises:
Alex Klein8b444532023-04-11 16:35:24 -0600522 ValueError if the first line of |sym_file| is invalid
Alex Klein1699fab2022-09-08 08:46:06 -0600523 """
524 with file_util.Open(sym_file, "rb") as f:
525 header = f.readline().decode("utf-8").split()
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400526
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -0800527 if len(header) != 5 or header[0] != "MODULE":
528 raise ValueError(
529 f"header of sym file from {name_for_errors} is invalid"
530 )
Mike Frysinger50cedd32014-02-09 23:03:18 -0500531
Alex Klein1699fab2022-09-08 08:46:06 -0600532 return SymbolHeader(
533 os=header[1], cpu=header[2], id=header[3], name=header[4]
534 )
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400535
536
Alex Klein1699fab2022-09-08 08:46:06 -0600537def GenerateBreakpadSymbol(
538 elf_file,
539 debug_file=None,
540 breakpad_dir=None,
541 strip_cfi=False,
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -0800542 sysroot=None,
Alex Klein1699fab2022-09-08 08:46:06 -0600543 num_errors=None,
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -0800544 found_files=None,
Alex Klein1699fab2022-09-08 08:46:06 -0600545 dump_syms_cmd="dump_syms",
David Rileyc1a44f62023-07-07 16:18:34 -0700546 dump_syms_args=None,
Ian Barkley-Yeungb5274442023-04-28 16:32:20 -0700547 force_basic_fallback=False,
Alex Klein1699fab2022-09-08 08:46:06 -0600548):
549 """Generate the symbols for |elf_file| using |debug_file|
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400550
Alex Klein1699fab2022-09-08 08:46:06 -0600551 Args:
Alex Klein8b444532023-04-11 16:35:24 -0600552 elf_file: The file to dump symbols for
553 debug_file: Split debug file to use for symbol information
554 breakpad_dir: The dir to store the output symbol file in
555 strip_cfi: Do not generate CFI data
556 sysroot: Path to the sysroot with the elf_file under it
557 num_errors: An object to update with the error count (needs a .value
558 member).
559 found_files: A multiprocessing.managers.ListProxy list containing
560 ExpectedFiles, representing which of the "should always be present"
561 files have been processed.
562 dump_syms_cmd: Command to use for dumping symbols.
David Rileyc1a44f62023-07-07 16:18:34 -0700563 dump_syms_args: List of args to pass to dump_syms_cmd.
564 If not specified, a reasonable default will be used.
Ian Barkley-Yeungb5274442023-04-28 16:32:20 -0700565 force_basic_fallback: If True, always use _DumpAllowingBasicFallback()
566 instead of _DumpExpectingSymbols().
Mike Frysinger1a736a82013-12-12 01:50:59 -0500567
Alex Klein1699fab2022-09-08 08:46:06 -0600568 Returns:
Alex Klein8b444532023-04-11 16:35:24 -0600569 The name of symbol file written out on success, or the failure count.
Alex Klein1699fab2022-09-08 08:46:06 -0600570 """
571 assert breakpad_dir
572 if num_errors is None:
Trent Apted1e2e4f32023-05-05 03:50:20 +0000573 num_errors = ctypes.c_int(0)
David Rileyc1a44f62023-07-07 16:18:34 -0700574 if dump_syms_args is None:
575 dump_syms_args = ["-v", "-d", "-m"]
Alex Klein1699fab2022-09-08 08:46:06 -0600576 debug_file_only = not os.path.exists(elf_file)
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400577
David Rileyc1a44f62023-07-07 16:18:34 -0700578 cmd_base = [dump_syms_cmd] + dump_syms_args
Alex Klein1699fab2022-09-08 08:46:06 -0600579 if strip_cfi:
580 cmd_base += ["-c"]
581 # Some files will not be readable by non-root (e.g. set*id /bin/su).
582 needs_sudo = not os.access(elf_file, os.R_OK)
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400583
Alex Klein1699fab2022-09-08 08:46:06 -0600584 def _DumpIt(cmd_args):
585 if needs_sudo:
586 run_command = cros_build_lib.sudo_run
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400587 else:
Alex Klein1699fab2022-09-08 08:46:06 -0600588 run_command = cros_build_lib.run
589 return run_command(
590 cmd_base + cmd_args,
591 stderr=True,
592 stdout=temp.name,
593 check=False,
594 debug_level=logging.DEBUG,
595 )
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400596
Alex Klein1699fab2022-09-08 08:46:06 -0600597 def _CrashCheck(result, file_or_files, msg):
598 if result.returncode:
599 cbuildbot_alerts.PrintBuildbotStepWarnings()
600 if result.returncode < 0:
601 logging.warning(
602 "dump_syms %s crashed with %s; %s",
603 file_or_files,
604 signals.StrSignal(-result.returncode),
605 msg,
606 )
607 else:
608 logging.warning(
609 "dump_syms %s returned %d; %s",
610 file_or_files,
611 result.returncode,
612 msg,
613 )
614 logging.warning("output:\n%s", result.stderr.decode("utf-8"))
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400615
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -0800616 def _DumpAllowingBasicFallback():
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -0800617 """Dump symbols for an ELF when we do NOT expect to get good symbols.
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -0800618
619 Returns:
620 A SymbolGenerationResult
621 """
Alex Klein1699fab2022-09-08 08:46:06 -0600622 if debug_file:
623 # Try to dump the symbols using the debug file like normal.
624 if debug_file_only:
625 cmd_args = [debug_file]
626 file_or_files = debug_file
627 else:
628 cmd_args = [elf_file, os.path.dirname(debug_file)]
629 file_or_files = [elf_file, debug_file]
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400630
Alex Klein1699fab2022-09-08 08:46:06 -0600631 result = _DumpIt(cmd_args)
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400632
Alex Klein1699fab2022-09-08 08:46:06 -0600633 if result.returncode:
634 # Sometimes dump_syms can crash because there's too much info.
635 # Try dumping and stripping the extended stuff out. At least
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -0800636 # this way we'll get the extended symbols.
637 # https://crbug.com/266064
Alex Klein1699fab2022-09-08 08:46:06 -0600638 _CrashCheck(result, file_or_files, "retrying w/out CFI")
639 cmd_args = ["-c", "-r"] + cmd_args
640 result = _DumpIt(cmd_args)
641 _CrashCheck(result, file_or_files, "retrying w/out debug")
Prathmesh Prabhu9995e9b2013-10-31 16:43:55 -0700642
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -0800643 if not result.returncode:
644 return SymbolGenerationResult.SUCCESS
Prathmesh Prabhu9995e9b2013-10-31 16:43:55 -0700645
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -0800646 # If that didn't work (no debug, or dump_syms still failed), try
647 # dumping just the file itself directly.
648 result = _DumpIt([elf_file])
649 if result.returncode:
650 # A lot of files (like kernel files) contain no debug information,
651 # do not consider such occurrences as errors.
652 cbuildbot_alerts.PrintBuildbotStepWarnings()
653 if b"file contains no debugging information" in result.stderr:
654 logging.warning("dump_syms failed; giving up entirely.")
655 logging.warning("No symbols found for %s", elf_file)
656 return SymbolGenerationResult.EXPECTED_FAILURE
657 else:
658 _CrashCheck(result, elf_file, "counting as failure")
659 return SymbolGenerationResult.UNEXPECTED_FAILURE
660
661 return SymbolGenerationResult.SUCCESS
662
663 def _DumpExpectingSymbols():
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -0800664 """Dump symbols for an ELF when we expect to get good symbols.
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -0800665
666 Returns:
667 A SymbolGenerationResult. We never expect failure, so the result
668 will always be SUCCESS or UNEXPECTED_FAILURE.
669 """
670 if not debug_file:
671 logging.warning("%s must have debug file", elf_file)
672 return SymbolGenerationResult.UNEXPECTED_FAILURE
673
674 cmd_args = [elf_file, os.path.dirname(debug_file)]
675 result = _DumpIt(cmd_args)
676 if result.returncode:
Ian Barkley-Yeungab1dab12023-04-21 18:19:55 -0700677 _CrashCheck(
678 result,
679 [elf_file, debug_file],
680 "unexpected symbol generation failure",
681 )
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -0800682 return SymbolGenerationResult.UNEXPECTED_FAILURE
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -0800683
684 # TODO(b/270240549): Remove try/except, allow exceptions to just
685 # fail the script. The try/except is just here until we are sure this
686 # will not break the build.
687 try:
688 if not ValidateSymbolFile(
689 temp.name, elf_file, sysroot, found_files
690 ):
691 logging.warning("%s: symbol file failed validation", elf_file)
692 return SymbolGenerationResult.UNEXPECTED_FAILURE
693 except ValueError as e:
694 logging.warning(
695 "%s: symbol file failed validation due to exception %s",
696 elf_file,
697 e,
698 )
699 return SymbolGenerationResult.UNEXPECTED_FAILURE
700
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -0800701 return SymbolGenerationResult.SUCCESS
702
703 osutils.SafeMakedirs(breakpad_dir)
704 with cros_build_lib.UnbufferedNamedTemporaryFile(
705 dir=breakpad_dir, delete=False
706 ) as temp:
Ian Barkley-Yeungb5274442023-04-28 16:32:20 -0700707 if not force_basic_fallback and _ExpectGoodSymbols(elf_file, sysroot):
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -0800708 result = _DumpExpectingSymbols()
709 # Until the EXPECTED_POOR_SYMBOLIZATION_FILES allowlist is
710 # completely set up for all boards, don't fail the build if
711 # _ExpectGoodSymbols is wrong.
712 # TODO(b/241470012): Remove the call to _DumpAllowingBasicFallback()
713 # and just error out if _DumpExpectingSymbols fails.
714 if result == SymbolGenerationResult.UNEXPECTED_FAILURE:
715 result = _DumpAllowingBasicFallback()
716 else:
717 result = _DumpAllowingBasicFallback()
718
719 if result == SymbolGenerationResult.UNEXPECTED_FAILURE:
720 num_errors.value += 1
721 os.unlink(temp.name)
722 return num_errors.value
723
724 if result == SymbolGenerationResult.EXPECTED_FAILURE:
725 os.unlink(temp.name)
726 return 0
Prathmesh Prabhu9995e9b2013-10-31 16:43:55 -0700727
Alex Klein1699fab2022-09-08 08:46:06 -0600728 # Move the dumped symbol file to the right place:
Alex Klein8b444532023-04-11 16:35:24 -0600729 # /$SYSROOT/usr/lib/debug/breakpad/<module-name>/<id>/<module-name>.sym
Ian Barkley-Yeungfdaaf2e2023-03-02 17:30:39 -0800730 header = ReadSymsHeader(temp, elf_file)
Alex Klein1699fab2022-09-08 08:46:06 -0600731 logging.info("Dumped %s as %s : %s", elf_file, header.name, header.id)
732 sym_file = os.path.join(
733 breakpad_dir, header.name, header.id, header.name + ".sym"
734 )
735 osutils.SafeMakedirs(os.path.dirname(sym_file))
736 os.rename(temp.name, sym_file)
737 os.chmod(sym_file, 0o644)
Prathmesh Prabhu9995e9b2013-10-31 16:43:55 -0700738
Alex Klein1699fab2022-09-08 08:46:06 -0600739 return sym_file
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400740
Prathmesh Prabhu9995e9b2013-10-31 16:43:55 -0700741
Alex Klein1699fab2022-09-08 08:46:06 -0600742def GenerateBreakpadSymbols(
743 board,
744 breakpad_dir=None,
745 strip_cfi=False,
746 generate_count=None,
747 sysroot=None,
748 num_processes=None,
749 clean_breakpad=False,
750 exclude_dirs=(),
751 file_list=None,
Ian Barkley-Yeungb5274442023-04-28 16:32:20 -0700752 always_use_basic_fallback=False,
753 ignore_expected_files=(),
Alex Klein1699fab2022-09-08 08:46:06 -0600754):
755 """Generate symbols for this board.
Prathmesh Prabhu9995e9b2013-10-31 16:43:55 -0700756
Alex Klein1699fab2022-09-08 08:46:06 -0600757 If |file_list| is None, symbols are generated for all executables, otherwise
758 only for the files included in |file_list|.
Prathmesh Prabhu9995e9b2013-10-31 16:43:55 -0700759
Alex Klein1699fab2022-09-08 08:46:06 -0600760 TODO(build):
761 This should be merged with buildbot_commands.GenerateBreakpadSymbols()
762 once we rewrite cros_generate_breakpad_symbols in python.
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400763
Alex Klein1699fab2022-09-08 08:46:06 -0600764 Args:
Alex Klein8b444532023-04-11 16:35:24 -0600765 board: The board whose symbols we wish to generate
766 breakpad_dir: The full path to the breakpad directory where symbols live
767 strip_cfi: Do not generate CFI data
768 generate_count: If set, only generate this many symbols (meant for
769 testing)
770 sysroot: The root where to find the corresponding ELFs
771 num_processes: Number of jobs to run in parallel
772 clean_breakpad: Should we `rm -rf` the breakpad output dir first; note:
773 we do not do any locking, so do not run more than one in parallel
774 when True
775 exclude_dirs: List of dirs (relative to |sysroot|) to not search
776 file_list: Only generate symbols for files in this list. Each file must
777 be a full path (including |sysroot| prefix).
778 TODO(build): Support paths w/o |sysroot|.
Ian Barkley-Yeungb5274442023-04-28 16:32:20 -0700779 always_use_basic_fallback: If True, use the "basic fallback" mode for
780 all symbol files.
781 ignore_expected_files: A list of ExpectedFiles that will not be
782 considered "missing" if we do not generate symbols for them.
Alex Klein1699fab2022-09-08 08:46:06 -0600783
784 Returns:
Alex Klein8b444532023-04-11 16:35:24 -0600785 The number of errors that were encountered.
Alex Klein1699fab2022-09-08 08:46:06 -0600786 """
787 if sysroot is None:
788 sysroot = build_target_lib.get_default_sysroot_path(board)
789 if breakpad_dir is None:
790 breakpad_dir = FindBreakpadDir(board, sysroot=sysroot)
791 if clean_breakpad:
792 logging.info("cleaning out %s first", breakpad_dir)
793 osutils.RmDir(breakpad_dir, ignore_missing=True, sudo=True)
794 # Make sure non-root can write out symbols as needed.
795 osutils.SafeMakedirs(breakpad_dir, sudo=True)
796 if not os.access(breakpad_dir, os.W_OK):
797 cros_build_lib.sudo_run(["chown", "-R", str(os.getuid()), breakpad_dir])
798 debug_dir = FindDebugDir(board, sysroot=sysroot)
799 exclude_paths = [os.path.join(debug_dir, x) for x in exclude_dirs]
800 if file_list is None:
801 file_list = []
802 file_filter = dict.fromkeys([os.path.normpath(x) for x in file_list], False)
803
804 logging.info("generating breakpad symbols using %s", debug_dir)
805
806 # Let's locate all the debug_files and elfs first along with the debug file
Alex Klein8b444532023-04-11 16:35:24 -0600807 # sizes. This way we can start processing the largest files first in
808 # parallel with the small ones.
Alex Klein1699fab2022-09-08 08:46:06 -0600809 # If |file_list| was given, ignore all other files.
810 targets = []
811 for root, dirs, files in os.walk(debug_dir):
812 if root in exclude_paths:
813 logging.info("Skipping excluded dir %s", root)
814 del dirs[:]
815 continue
816
817 for debug_file in files:
818 debug_file = os.path.join(root, debug_file)
819 # Turn /build/$BOARD/usr/lib/debug/sbin/foo.debug into
820 # /build/$BOARD/sbin/foo.
821 elf_file = os.path.join(
822 sysroot, debug_file[len(debug_dir) + 1 : -6]
823 )
824
825 if file_filter:
826 if elf_file in file_filter:
827 file_filter[elf_file] = True
828 elif debug_file in file_filter:
829 file_filter[debug_file] = True
830 else:
831 continue
832
833 # Filter out files based on common issues with the debug file.
834 if not debug_file.endswith(".debug"):
835 continue
836
837 elif os.path.islink(debug_file):
838 # The build-id stuff is common enough to filter out by default.
839 if "/.build-id/" in debug_file:
840 msg = logging.debug
841 else:
842 msg = logging.warning
843 msg("Skipping symbolic link %s", debug_file)
844 continue
845
846 # Filter out files based on common issues with the elf file.
847 elf_path = os.path.relpath(elf_file, sysroot)
848 debug_only = elf_path in ALLOWED_DEBUG_ONLY_FILES
849 if not os.path.exists(elf_file) and not debug_only:
Alex Klein8b444532023-04-11 16:35:24 -0600850 # Sometimes we filter out programs from /usr/bin but leave
851 # behind the .debug file.
Alex Klein1699fab2022-09-08 08:46:06 -0600852 logging.warning("Skipping missing %s", elf_file)
853 continue
854
855 targets.append((os.path.getsize(debug_file), elf_file, debug_file))
856
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -0800857 with multiprocessing.Manager() as mp_manager:
858 bg_errors = parallel.WrapMultiprocessing(multiprocessing.Value, "i")
859 found_files = parallel.WrapMultiprocessing(mp_manager.list)
860 if file_filter:
861 files_not_found = [
862 x for x, found in file_filter.items() if not found
863 ]
864 bg_errors.value += len(files_not_found)
865 if files_not_found:
866 logging.error(
867 "Failed to find requested files: %s", files_not_found
868 )
Alex Klein1699fab2022-09-08 08:46:06 -0600869
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -0800870 # Now start generating symbols for the discovered elfs.
871 with parallel.BackgroundTaskRunner(
872 GenerateBreakpadSymbol,
873 breakpad_dir=breakpad_dir,
874 strip_cfi=strip_cfi,
875 num_errors=bg_errors,
876 processes=num_processes,
877 sysroot=sysroot,
878 found_files=found_files,
Ian Barkley-Yeungb5274442023-04-28 16:32:20 -0700879 force_basic_fallback=always_use_basic_fallback,
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -0800880 ) as queue:
881 for _, elf_file, debug_file in sorted(targets, reverse=True):
Alex Klein1699fab2022-09-08 08:46:06 -0600882 if generate_count == 0:
883 break
884
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -0800885 queue.put([elf_file, debug_file])
886 if generate_count is not None:
887 generate_count -= 1
888 if generate_count == 0:
889 break
890
Ian Barkley-Yeungb5274442023-04-28 16:32:20 -0700891 missing = (
892 ALL_EXPECTED_FILES
893 - frozenset(found_files)
894 - frozenset(ignore_expected_files)
895 )
896 if (
897 missing
898 and not file_filter
899 and generate_count is None
900 and not always_use_basic_fallback
901 ):
Ian Barkley-Yeungc5b6f582023-03-08 18:23:07 -0800902 logging.warning(
903 "Not all expected files were processed successfully, "
904 "missing %s",
905 missing,
906 )
907 # TODO(b/270240549): Increment bg_errors.value here once we check
908 # that this isn't going to fail any current builds.
909
Alex Klein1699fab2022-09-08 08:46:06 -0600910 return bg_errors.value
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400911
912
Mike Frysinger3f571af2016-08-31 23:56:53 -0400913def FindDebugDir(board, sysroot=None):
Alex Klein1699fab2022-09-08 08:46:06 -0600914 """Given a |board|, return the path to the split debug dir for it"""
915 if sysroot is None:
916 sysroot = build_target_lib.get_default_sysroot_path(board)
917 return os.path.join(sysroot, "usr", "lib", "debug")
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400918
919
Mike Frysinger3f571af2016-08-31 23:56:53 -0400920def FindBreakpadDir(board, sysroot=None):
Alex Klein1699fab2022-09-08 08:46:06 -0600921 """Given a |board|, return the path to the breakpad dir for it"""
922 return os.path.join(FindDebugDir(board, sysroot=sysroot), "breakpad")
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400923
924
925def main(argv):
Alex Klein1699fab2022-09-08 08:46:06 -0600926 parser = commandline.ArgumentParser(description=__doc__)
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400927
Alex Klein1699fab2022-09-08 08:46:06 -0600928 parser.add_argument(
929 "--board", default=None, help="board to generate symbols for"
930 )
931 parser.add_argument(
932 "--breakpad_root",
933 type="path",
934 default=None,
935 help="root output directory for breakpad symbols",
936 )
937 parser.add_argument(
938 "--sysroot",
939 type="path",
940 default=None,
941 help="root input directory for files",
942 )
943 parser.add_argument(
944 "--exclude-dir",
945 type=str,
946 action="append",
947 default=[],
948 help="directory (relative to |board| root) to not search",
949 )
950 parser.add_argument(
951 "--generate-count",
952 type=int,
953 default=None,
954 help="only generate # number of symbols",
955 )
956 parser.add_argument(
957 "--noclean",
958 dest="clean",
959 action="store_false",
960 default=True,
961 help="do not clean out breakpad dir before running",
962 )
963 parser.add_argument(
964 "--jobs", type=int, default=None, help="limit number of parallel jobs"
965 )
966 parser.add_argument(
967 "--strip_cfi",
968 action="store_true",
969 default=False,
970 help="do not generate CFI data (pass -c to dump_syms)",
971 )
972 parser.add_argument(
Ian Barkley-Yeungb5274442023-04-28 16:32:20 -0700973 "--ignore_errors",
974 action="store_true",
975 default=False,
976 help="Ignore errors from dump_syms, do not validate symbol files, "
977 "just generate symbols best effort",
978 )
979 parser.add_argument(
980 "--ignore_expected_file",
981 type=str,
982 action="append",
983 default=[],
984 choices=[x.name for x in ExpectedFiles],
985 help="do not generate errors if symbols are not generated for these "
986 "files",
987 )
988 parser.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600989 "file_list",
990 nargs="*",
991 default=None,
Trent Aptedc4f366a2023-05-16 15:32:48 +1000992 help=(
993 "generate symbols for only these files "
994 "(e.g. /build/$BOARD/usr/bin/foo)"
995 ),
Alex Klein1699fab2022-09-08 08:46:06 -0600996 )
Mike Frysinger69cb41d2013-08-11 20:08:19 -0400997
Alex Klein1699fab2022-09-08 08:46:06 -0600998 opts = parser.parse_args(argv)
999 opts.Freeze()
Ian Barkley-Yeungb5274442023-04-28 16:32:20 -07001000 ignore_expected_files = [
1001 ExpectedFiles[x] for x in opts.ignore_expected_file
1002 ]
Mike Frysinger69cb41d2013-08-11 20:08:19 -04001003
Alex Klein1699fab2022-09-08 08:46:06 -06001004 if opts.board is None and opts.sysroot is None:
1005 cros_build_lib.Die("--board or --sysroot is required")
Mike Frysinger69cb41d2013-08-11 20:08:19 -04001006
Alex Klein1699fab2022-09-08 08:46:06 -06001007 ret = GenerateBreakpadSymbols(
1008 opts.board,
1009 breakpad_dir=opts.breakpad_root,
1010 strip_cfi=opts.strip_cfi,
1011 generate_count=opts.generate_count,
1012 sysroot=opts.sysroot,
1013 num_processes=opts.jobs,
1014 clean_breakpad=opts.clean,
1015 exclude_dirs=opts.exclude_dir,
1016 file_list=opts.file_list,
Ian Barkley-Yeungb5274442023-04-28 16:32:20 -07001017 always_use_basic_fallback=opts.ignore_errors,
1018 ignore_expected_files=ignore_expected_files,
Alex Klein1699fab2022-09-08 08:46:06 -06001019 )
1020 if ret:
1021 logging.error("encountered %i problem(s)", ret)
Alex Klein8b444532023-04-11 16:35:24 -06001022 # Since exit(status) gets masked, clamp it to 1 so we don't
1023 # inadvertently return 0 in case we are a multiple of the mask.
Alex Klein1699fab2022-09-08 08:46:06 -06001024 ret = 1
Mike Frysinger69cb41d2013-08-11 20:08:19 -04001025
Ian Barkley-Yeungb5274442023-04-28 16:32:20 -07001026 if opts.ignore_errors:
1027 return 0
1028
Alex Klein1699fab2022-09-08 08:46:06 -06001029 return ret