1*9c5db199SXin Li# Copyright (c) 2012 The Chromium OS Authors. All rights reserved. 2*9c5db199SXin Li# Use of this source code is governed by a BSD-style license that can be 3*9c5db199SXin Li# found in the LICENSE file. 4*9c5db199SXin Li 5*9c5db199SXin Liimport glob 6*9c5db199SXin Liimport logging 7*9c5db199SXin Liimport os 8*9c5db199SXin Liimport random 9*9c5db199SXin Liimport re 10*9c5db199SXin Liimport shutil 11*9c5db199SXin Liimport time 12*9c5db199SXin Li 13*9c5db199SXin Liimport common 14*9c5db199SXin Lifrom autotest_lib.client.bin import test, utils 15*9c5db199SXin Lifrom autotest_lib.client.common_lib import error 16*9c5db199SXin Lifrom autotest_lib.client.cros import constants, cros_logging 17*9c5db199SXin Li 18*9c5db199SXin Li 19*9c5db199SXin Li_CRASH_RUN_STATE_DIR = '/run/crash_reporter' 20*9c5db199SXin Li 21*9c5db199SXin Li 22*9c5db199SXin Liclass FilterOut: 23*9c5db199SXin Li """contextmanager-compatible class to block certain crashes during tests.""" 24*9c5db199SXin Li 25*9c5db199SXin Li def __init__(self, name): 26*9c5db199SXin Li self._FILTER_OUT = _CRASH_RUN_STATE_DIR + '/filter-out' 27*9c5db199SXin Li self.name = name 28*9c5db199SXin Li 29*9c5db199SXin Li def __enter__(self): 30*9c5db199SXin Li """Writes the given parameter to the filter-out file. 31*9c5db199SXin Li 32*9c5db199SXin Li This is used to ignore crashes in which we have no interest. 33*9c5db199SXin Li """ 34*9c5db199SXin Li utils.open_write_close(self._FILTER_OUT, self.name) 35*9c5db199SXin Li 36*9c5db199SXin Li def __exit__(self, ex_type, value, traceback): 37*9c5db199SXin Li """Remove the filter-out file. 38*9c5db199SXin Li 39*9c5db199SXin Li Next time the crash reporter is invoked, it will not filter crashes.""" 40*9c5db199SXin Li os.remove(self._FILTER_OUT) 41*9c5db199SXin Li # Do *not* handle any exception 42*9c5db199SXin Li return False 43*9c5db199SXin Li 44*9c5db199SXin Li 45*9c5db199SXin Liclass CrashTest(test.test): 46*9c5db199SXin Li """ 47*9c5db199SXin Li This class deals with running crash tests, which are tests which crash a 48*9c5db199SXin Li user-space program (or the whole machine) and generate a core dump. We 49*9c5db199SXin Li want to check that the correct crash dump is available and can be 50*9c5db199SXin Li retrieved. 51*9c5db199SXin Li 52*9c5db199SXin Li Chromium OS has a crash sender which checks for new crash data and sends 53*9c5db199SXin Li it to a server. This crash data is used to track software quality and find 54*9c5db199SXin Li bugs. The system crash sender normally is always running, but can be paused 55*9c5db199SXin Li by creating _PAUSE_FILE. When crash sender sees this, it pauses operation. 56*9c5db199SXin Li 57*9c5db199SXin Li For testing purposes we sometimes want to run the crash sender manually. 58*9c5db199SXin Li In this case we can pass the --ignore_pause_file flag and run the crash 59*9c5db199SXin Li sender manually. 60*9c5db199SXin Li 61*9c5db199SXin Li Also for testing we sometimes want to mock out the crash sender, and just 62*9c5db199SXin Li have it pretend to succeed or fail. The _MOCK_CRASH_SENDING file is used 63*9c5db199SXin Li for this. If it doesn't exist, then the crash sender runs normally. If 64*9c5db199SXin Li it exists but is empty, the crash sender will succeed (but actually do 65*9c5db199SXin Li nothing). If the file contains something, then the crash sender will fail. 66*9c5db199SXin Li 67*9c5db199SXin Li If the user consents to sending crash tests, then the _CONSENT_FILE will 68*9c5db199SXin Li exist in the home directory. This test needs to create this file for the 69*9c5db199SXin Li crash sending to work. The metrics daemon caches the consent state for 70*9c5db199SXin Li 1 second, so we need to sleep for more than that after changing it to be 71*9c5db199SXin Li sure it picks up the change. 72*9c5db199SXin Li 73*9c5db199SXin Li Crash reports are rate limited to a certain number of reports each 24 74*9c5db199SXin Li hours. If the maximum number has already been sent then reports are held 75*9c5db199SXin Li until later. This is administered by a directory _CRASH_SENDER_RATE_DIR 76*9c5db199SXin Li which contains one temporary file for each time a report is sent. 77*9c5db199SXin Li 78*9c5db199SXin Li The class provides the ability to push a consent file. This disables 79*9c5db199SXin Li consent for this test but allows it to be popped back at later. This 80*9c5db199SXin Li makes nested tests easier. If _automatic_consent_saving is True (the 81*9c5db199SXin Li default) then consent will be pushed at the start and popped at the end. 82*9c5db199SXin Li 83*9c5db199SXin Li Interesting variables: 84*9c5db199SXin Li _log_reader: the log reader used for reading log files 85*9c5db199SXin Li _leave_crash_sending: True to enable crash sending on exit from the 86*9c5db199SXin Li test, False to disable it. (Default True). 87*9c5db199SXin Li _automatic_consent_saving: True to push the consent at the start of 88*9c5db199SXin Li the test and pop it afterwards. (Default True). 89*9c5db199SXin Li 90*9c5db199SXin Li Useful places to look for more information are: 91*9c5db199SXin Li 92*9c5db199SXin Li chromeos/src/platform/crash-reporter/crash_sender 93*9c5db199SXin Li - sender script which crash crash reporter to create reports, then 94*9c5db199SXin Li 95*9c5db199SXin Li chromeos/src/platform/crash-reporter/ 96*9c5db199SXin Li - crash reporter program 97*9c5db199SXin Li """ 98*9c5db199SXin Li 99*9c5db199SXin Li 100*9c5db199SXin Li _CONSENT_FILE = '/home/chronos/Consent To Send Stats' 101*9c5db199SXin Li _CORE_PATTERN = '/proc/sys/kernel/core_pattern' 102*9c5db199SXin Li _LOCK_CORE_PATTERN = '/proc/sys/kernel/lock_core_pattern' 103*9c5db199SXin Li _CRASH_REPORTER_PATH = '/sbin/crash_reporter' 104*9c5db199SXin Li _CRASH_SENDER_PATH = '/sbin/crash_sender' 105*9c5db199SXin Li _CRASH_SENDER_RATE_DIR = '/var/lib/crash_sender' 106*9c5db199SXin Li _CRASH_SENDER_LOCK_PATH = '/run/lock/crash_sender' 107*9c5db199SXin Li _CRASH_TEST_IN_PROGRESS = _CRASH_RUN_STATE_DIR + '/crash-test-in-progress' 108*9c5db199SXin Li _MOCK_CRASH_SENDING = _CRASH_RUN_STATE_DIR + '/mock-crash-sending' 109*9c5db199SXin Li _FILTER_IN = _CRASH_RUN_STATE_DIR + '/filter-in' 110*9c5db199SXin Li _PAUSE_FILE = '/var/lib/crash_sender_paused' 111*9c5db199SXin Li _SYSTEM_CRASH_DIR = '/var/spool/crash' 112*9c5db199SXin Li _FALLBACK_USER_CRASH_DIR = '/home/chronos/crash' 113*9c5db199SXin Li _REBOOT_VAULT_CRASH_DIR = '/mnt/stateful_partition/reboot_vault/crash' 114*9c5db199SXin Li _USER_CRASH_DIRS = '/home/chronos/u-*/crash' 115*9c5db199SXin Li _USER_CRASH_DIR_REGEX = re.compile('/home/chronos/u-([a-f0-9]+)/crash') 116*9c5db199SXin Li 117*9c5db199SXin Li # Matches kDefaultMaxUploadBytes 118*9c5db199SXin Li _MAX_CRASH_SIZE = 1024 * 1024 119*9c5db199SXin Li 120*9c5db199SXin Li # Use the same file format as crash does normally: 121*9c5db199SXin Li # <basename>.#.#.#.#.meta 122*9c5db199SXin Li _FAKE_TEST_BASENAME = 'fake.1.2.3.4' 123*9c5db199SXin Li 124*9c5db199SXin Li def _set_system_sending(self, is_enabled): 125*9c5db199SXin Li """Sets whether or not the system crash_sender is allowed to run. 126*9c5db199SXin Li 127*9c5db199SXin Li This is done by creating or removing _PAUSE_FILE. 128*9c5db199SXin Li 129*9c5db199SXin Li crash_sender may still be allowed to run if _call_sender_one_crash is 130*9c5db199SXin Li called with 'ignore_pause=True'. 131*9c5db199SXin Li 132*9c5db199SXin Li @param is_enabled: True to enable crash_sender, False to disable it. 133*9c5db199SXin Li """ 134*9c5db199SXin Li if is_enabled: 135*9c5db199SXin Li if os.path.exists(self._PAUSE_FILE): 136*9c5db199SXin Li os.remove(self._PAUSE_FILE) 137*9c5db199SXin Li else: 138*9c5db199SXin Li utils.system('touch ' + self._PAUSE_FILE) 139*9c5db199SXin Li 140*9c5db199SXin Li def _remove_all_files_in_dir(self, d): 141*9c5db199SXin Li """Recursively remove all of the files in |d|, without removing |d|. 142*9c5db199SXin Li """ 143*9c5db199SXin Li try: 144*9c5db199SXin Li root, dirs, files = next(os.walk(d)) 145*9c5db199SXin Li except StopIteration: 146*9c5db199SXin Li return 147*9c5db199SXin Li for path in files: 148*9c5db199SXin Li os.remove(os.path.join(root, path)) 149*9c5db199SXin Li for path in dirs: 150*9c5db199SXin Li shutil.rmtree(os.path.join(root, path)) 151*9c5db199SXin Li 152*9c5db199SXin Li 153*9c5db199SXin Li def _reset_rate_limiting(self): 154*9c5db199SXin Li """Reset the count of crash reports sent today. 155*9c5db199SXin Li 156*9c5db199SXin Li This clears the contents of the rate limiting directory which has 157*9c5db199SXin Li the effect of reseting our count of crash reports sent. 158*9c5db199SXin Li """ 159*9c5db199SXin Li self._remove_all_files_in_dir(self._CRASH_SENDER_RATE_DIR) 160*9c5db199SXin Li 161*9c5db199SXin Li 162*9c5db199SXin Li def _clear_spooled_crashes(self): 163*9c5db199SXin Li """Clears system and user crash directories. 164*9c5db199SXin Li 165*9c5db199SXin Li This will remove all crash reports which are waiting to be sent. 166*9c5db199SXin Li """ 167*9c5db199SXin Li self._remove_all_files_in_dir(self._SYSTEM_CRASH_DIR) 168*9c5db199SXin Li self._remove_all_files_in_dir(self._REBOOT_VAULT_CRASH_DIR) 169*9c5db199SXin Li for d in glob.glob(self._USER_CRASH_DIRS): 170*9c5db199SXin Li self._remove_all_files_in_dir(d) 171*9c5db199SXin Li self._remove_all_files_in_dir(self._FALLBACK_USER_CRASH_DIR) 172*9c5db199SXin Li 173*9c5db199SXin Li 174*9c5db199SXin Li def _kill_running_sender(self): 175*9c5db199SXin Li """Kill the the crash_sender process if running.""" 176*9c5db199SXin Li utils.system('pkill -9 -e --exact crash_sender', ignore_status=True) 177*9c5db199SXin Li 178*9c5db199SXin Li 179*9c5db199SXin Li def _set_sending_mock(self, mock_enabled): 180*9c5db199SXin Li """Enables / disables mocking of the sending process. 181*9c5db199SXin Li 182*9c5db199SXin Li This uses the _MOCK_CRASH_SENDING file to achieve its aims. See notes 183*9c5db199SXin Li at the top. 184*9c5db199SXin Li 185*9c5db199SXin Li @param mock_enabled: If True, mocking is enabled, else it is disabled. 186*9c5db199SXin Li """ 187*9c5db199SXin Li if mock_enabled: 188*9c5db199SXin Li data = '' 189*9c5db199SXin Li logging.info('Setting sending mock') 190*9c5db199SXin Li utils.open_write_close(self._MOCK_CRASH_SENDING, data) 191*9c5db199SXin Li else: 192*9c5db199SXin Li utils.system('rm -f ' + self._MOCK_CRASH_SENDING) 193*9c5db199SXin Li 194*9c5db199SXin Li 195*9c5db199SXin Li def _set_consent(self, has_consent): 196*9c5db199SXin Li """Sets whether or not we have consent to send crash reports. 197*9c5db199SXin Li 198*9c5db199SXin Li This creates or deletes the _CONSENT_FILE to control whether 199*9c5db199SXin Li crash_sender will consider that it has consent to send crash reports. 200*9c5db199SXin Li It also copies a policy blob with the proper policy setting. 201*9c5db199SXin Li 202*9c5db199SXin Li @param has_consent: True to indicate consent, False otherwise 203*9c5db199SXin Li """ 204*9c5db199SXin Li autotest_cros_dir = os.path.join(os.path.dirname(__file__), '..') 205*9c5db199SXin Li if has_consent: 206*9c5db199SXin Li if os.path.isdir(constants.DEVICESETTINGS_DIR): 207*9c5db199SXin Li # Create policy file that enables metrics/consent. 208*9c5db199SXin Li shutil.copy('%s/mock_metrics_on.policy' % autotest_cros_dir, 209*9c5db199SXin Li constants.SIGNED_POLICY_FILE) 210*9c5db199SXin Li shutil.copy('%s/mock_metrics_owner.key' % autotest_cros_dir, 211*9c5db199SXin Li constants.OWNER_KEY_FILE) 212*9c5db199SXin Li # Create deprecated consent file. This is created *after* the 213*9c5db199SXin Li # policy file in order to avoid a race condition where chrome 214*9c5db199SXin Li # might remove the consent file if the policy's not set yet. 215*9c5db199SXin Li # We create it as a temp file first in order to make the creation 216*9c5db199SXin Li # of the consent file, owned by chronos, atomic. 217*9c5db199SXin Li # See crosbug.com/18413. 218*9c5db199SXin Li temp_file = self._CONSENT_FILE + '.tmp'; 219*9c5db199SXin Li utils.open_write_close(temp_file, 'test-consent') 220*9c5db199SXin Li utils.system('chown chronos:chronos "%s"' % (temp_file)) 221*9c5db199SXin Li shutil.move(temp_file, self._CONSENT_FILE) 222*9c5db199SXin Li logging.info('Created %s', self._CONSENT_FILE) 223*9c5db199SXin Li else: 224*9c5db199SXin Li if os.path.isdir(constants.DEVICESETTINGS_DIR): 225*9c5db199SXin Li # Create policy file that disables metrics/consent. 226*9c5db199SXin Li shutil.copy('%s/mock_metrics_off.policy' % autotest_cros_dir, 227*9c5db199SXin Li constants.SIGNED_POLICY_FILE) 228*9c5db199SXin Li shutil.copy('%s/mock_metrics_owner.key' % autotest_cros_dir, 229*9c5db199SXin Li constants.OWNER_KEY_FILE) 230*9c5db199SXin Li # Remove deprecated consent file. 231*9c5db199SXin Li utils.system('rm -f "%s"' % (self._CONSENT_FILE)) 232*9c5db199SXin Li # Ensure cached consent state is updated. 233*9c5db199SXin Li time.sleep(2) 234*9c5db199SXin Li 235*9c5db199SXin Li 236*9c5db199SXin Li def _set_crash_test_in_progress(self, in_progress): 237*9c5db199SXin Li if in_progress: 238*9c5db199SXin Li utils.open_write_close(self._CRASH_TEST_IN_PROGRESS, 'in-progress') 239*9c5db199SXin Li logging.info('Created %s', self._CRASH_TEST_IN_PROGRESS) 240*9c5db199SXin Li else: 241*9c5db199SXin Li utils.system('rm -f "%s"' % (self._CRASH_TEST_IN_PROGRESS)) 242*9c5db199SXin Li 243*9c5db199SXin Li 244*9c5db199SXin Li def _get_pushed_consent_file_path(self): 245*9c5db199SXin Li """Returns filename of the pushed consent file.""" 246*9c5db199SXin Li return os.path.join(self.bindir, 'pushed_consent') 247*9c5db199SXin Li 248*9c5db199SXin Li 249*9c5db199SXin Li def _get_pushed_policy_file_path(self): 250*9c5db199SXin Li """Returns filename of the pushed policy file.""" 251*9c5db199SXin Li return os.path.join(self.bindir, 'pushed_policy') 252*9c5db199SXin Li 253*9c5db199SXin Li 254*9c5db199SXin Li def _get_pushed_owner_key_file_path(self): 255*9c5db199SXin Li """Returns filename of the pushed owner.key file.""" 256*9c5db199SXin Li return os.path.join(self.bindir, 'pushed_owner_key') 257*9c5db199SXin Li 258*9c5db199SXin Li 259*9c5db199SXin Li def _push_consent(self): 260*9c5db199SXin Li """Push the consent file, thus disabling consent. 261*9c5db199SXin Li 262*9c5db199SXin Li The consent files can be created in the new test if required. Call 263*9c5db199SXin Li _pop_consent() to restore the original state. 264*9c5db199SXin Li """ 265*9c5db199SXin Li if os.path.exists(self._CONSENT_FILE): 266*9c5db199SXin Li shutil.move(self._CONSENT_FILE, 267*9c5db199SXin Li self._get_pushed_consent_file_path()) 268*9c5db199SXin Li if os.path.exists(constants.SIGNED_POLICY_FILE): 269*9c5db199SXin Li shutil.move(constants.SIGNED_POLICY_FILE, 270*9c5db199SXin Li self._get_pushed_policy_file_path()) 271*9c5db199SXin Li if os.path.exists(constants.OWNER_KEY_FILE): 272*9c5db199SXin Li shutil.move(constants.OWNER_KEY_FILE, 273*9c5db199SXin Li self._get_pushed_owner_key_file_path()) 274*9c5db199SXin Li # Ensure cached consent state is updated. 275*9c5db199SXin Li time.sleep(2) 276*9c5db199SXin Li 277*9c5db199SXin Li 278*9c5db199SXin Li def _pop_consent(self): 279*9c5db199SXin Li """Pop the consent files, enabling/disabling consent as it was before 280*9c5db199SXin Li we pushed the consent.""" 281*9c5db199SXin Li if os.path.exists(self._get_pushed_consent_file_path()): 282*9c5db199SXin Li shutil.move(self._get_pushed_consent_file_path(), 283*9c5db199SXin Li self._CONSENT_FILE) 284*9c5db199SXin Li else: 285*9c5db199SXin Li utils.system('rm -f "%s"' % self._CONSENT_FILE) 286*9c5db199SXin Li if os.path.exists(self._get_pushed_policy_file_path()): 287*9c5db199SXin Li shutil.move(self._get_pushed_policy_file_path(), 288*9c5db199SXin Li constants.SIGNED_POLICY_FILE) 289*9c5db199SXin Li else: 290*9c5db199SXin Li utils.system('rm -f "%s"' % constants.SIGNED_POLICY_FILE) 291*9c5db199SXin Li if os.path.exists(self._get_pushed_owner_key_file_path()): 292*9c5db199SXin Li shutil.move(self._get_pushed_owner_key_file_path(), 293*9c5db199SXin Li constants.OWNER_KEY_FILE) 294*9c5db199SXin Li else: 295*9c5db199SXin Li utils.system('rm -f "%s"' % constants.OWNER_KEY_FILE) 296*9c5db199SXin Li # Ensure cached consent state is updated. 297*9c5db199SXin Li time.sleep(2) 298*9c5db199SXin Li 299*9c5db199SXin Li 300*9c5db199SXin Li def _get_crash_dir(self, username, force_user_crash_dir=False): 301*9c5db199SXin Li """Returns crash directory for process running as the given user. 302*9c5db199SXin Li 303*9c5db199SXin Li @param username: Unix user of the crashing process. 304*9c5db199SXin Li @param force_user_crash_dir: Regardless of |username|, return the crash 305*9c5db199SXin Li directory of the current user session, or 306*9c5db199SXin Li the fallback directory if no sessions. 307*9c5db199SXin Li """ 308*9c5db199SXin Li if username in ('root', 'crash') and not force_user_crash_dir: 309*9c5db199SXin Li return self._SYSTEM_CRASH_DIR 310*9c5db199SXin Li else: 311*9c5db199SXin Li dirs = glob.glob(self._USER_CRASH_DIRS) 312*9c5db199SXin Li return dirs[0] if dirs else self._FALLBACK_USER_CRASH_DIR 313*9c5db199SXin Li 314*9c5db199SXin Li 315*9c5db199SXin Li def _canonicalize_crash_dir(self, crash_dir): 316*9c5db199SXin Li """Converts /home/chronos crash directory to /home/user counterpart. 317*9c5db199SXin Li 318*9c5db199SXin Li @param crash_dir: A path of the form /home/chronos/u-<hash>/crash. 319*9c5db199SXin Li @returns /home/user/<hash>/crash, or |crash_dir| on form mismatch. 320*9c5db199SXin Li """ 321*9c5db199SXin Li match = re.match(self._USER_CRASH_DIR_REGEX, crash_dir) 322*9c5db199SXin Li return ('/home/user/%s/crash' % match.group(1)) if match else crash_dir 323*9c5db199SXin Li 324*9c5db199SXin Li 325*9c5db199SXin Li def _initialize_crash_reporter(self, lock_core_pattern): 326*9c5db199SXin Li """Start up the crash reporter. 327*9c5db199SXin Li 328*9c5db199SXin Li @param lock_core_pattern: lock core pattern during initialization. 329*9c5db199SXin Li """ 330*9c5db199SXin Li 331*9c5db199SXin Li if not lock_core_pattern: 332*9c5db199SXin Li self._set_crash_test_in_progress(False) 333*9c5db199SXin Li utils.system('%s --init' % self._CRASH_REPORTER_PATH) 334*9c5db199SXin Li if not lock_core_pattern: 335*9c5db199SXin Li self._set_crash_test_in_progress(True) 336*9c5db199SXin Li # Completely disable crash_reporter from generating crash dumps 337*9c5db199SXin Li # while any tests are running, otherwise a crashy system can make 338*9c5db199SXin Li # these tests flaky. 339*9c5db199SXin Li self.enable_crash_filtering('none') 340*9c5db199SXin Li 341*9c5db199SXin Li 342*9c5db199SXin Li def get_crash_dir_name(self, name): 343*9c5db199SXin Li """Return the full path for |name| inside the system crash directory.""" 344*9c5db199SXin Li return os.path.join(self._SYSTEM_CRASH_DIR, name) 345*9c5db199SXin Li 346*9c5db199SXin Li 347*9c5db199SXin Li def write_crash_dir_entry(self, name, contents): 348*9c5db199SXin Li """Writes a file to the system crash directory. 349*9c5db199SXin Li 350*9c5db199SXin Li This writes a file to _SYSTEM_CRASH_DIR with the given name. This is 351*9c5db199SXin Li used to insert new crash dump files for testing purposes. 352*9c5db199SXin Li 353*9c5db199SXin Li If contents is not a string, binary data is assumed. 354*9c5db199SXin Li 355*9c5db199SXin Li @param name: Name of file to write. 356*9c5db199SXin Li @param contents: String/binary data to write to the file. 357*9c5db199SXin Li """ 358*9c5db199SXin Li entry = self.get_crash_dir_name(name) 359*9c5db199SXin Li if not os.path.exists(self._SYSTEM_CRASH_DIR): 360*9c5db199SXin Li os.makedirs(self._SYSTEM_CRASH_DIR) 361*9c5db199SXin Li 362*9c5db199SXin Li is_binary = not isinstance(contents, str) 363*9c5db199SXin Li utils.open_write_close(entry, contents, is_binary) 364*9c5db199SXin Li 365*9c5db199SXin Li return entry 366*9c5db199SXin Li 367*9c5db199SXin Li 368*9c5db199SXin Li def write_fake_meta(self, name, exec_name, payload, complete=True): 369*9c5db199SXin Li """Writes a fake meta entry to the system crash directory. 370*9c5db199SXin Li 371*9c5db199SXin Li @param name: Name of file to write. 372*9c5db199SXin Li @param exec_name: Value for exec_name item. 373*9c5db199SXin Li @param payload: Value for payload item. 374*9c5db199SXin Li @param complete: True to close off the record, otherwise leave it 375*9c5db199SXin Li incomplete. 376*9c5db199SXin Li """ 377*9c5db199SXin Li last_line = '' 378*9c5db199SXin Li if complete: 379*9c5db199SXin Li last_line = 'done=1\n' 380*9c5db199SXin Li contents = ('exec_name=%s\n' 381*9c5db199SXin Li 'ver=my_ver\n' 382*9c5db199SXin Li 'payload=%s\n' 383*9c5db199SXin Li '%s' % (exec_name, payload, 384*9c5db199SXin Li last_line)) 385*9c5db199SXin Li return self.write_crash_dir_entry(name, contents) 386*9c5db199SXin Li 387*9c5db199SXin Li def _get_dmp_contents(self): 388*9c5db199SXin Li """Creates the contents of the dmp file for our made crashes. 389*9c5db199SXin Li 390*9c5db199SXin Li The dmp file contents are deliberately large and hard-to-compress. This 391*9c5db199SXin Li ensures logging_CrashSender hits its bytes/day cap before its sends/day 392*9c5db199SXin Li cap. 393*9c5db199SXin Li """ 394*9c5db199SXin Li return bytearray( 395*9c5db199SXin Li [random.randint(0, 255) for n in range(self._MAX_CRASH_SIZE)]) 396*9c5db199SXin Li 397*9c5db199SXin Li 398*9c5db199SXin Li def _prepare_sender_one_crash(self, 399*9c5db199SXin Li reports_enabled, 400*9c5db199SXin Li report): 401*9c5db199SXin Li """Create metadata for a fake crash report. 402*9c5db199SXin Li 403*9c5db199SXin Li This enabled mocking of the crash sender, then creates a fake 404*9c5db199SXin Li crash report for testing purposes. 405*9c5db199SXin Li 406*9c5db199SXin Li @param reports_enabled: True to enable consent to that reports will be 407*9c5db199SXin Li sent. 408*9c5db199SXin Li @param report: Report to use for crash, if None we create one. 409*9c5db199SXin Li """ 410*9c5db199SXin Li self._set_sending_mock(mock_enabled=True) 411*9c5db199SXin Li self._set_consent(reports_enabled) 412*9c5db199SXin Li if report is None: 413*9c5db199SXin Li # Use the same file format as crash does normally: 414*9c5db199SXin Li # <basename>.#.#.#.meta 415*9c5db199SXin Li payload = os.path.basename( 416*9c5db199SXin Li self.write_crash_dir_entry( 417*9c5db199SXin Li '%s.dmp' % self._FAKE_TEST_BASENAME, 418*9c5db199SXin Li self._get_dmp_contents())) 419*9c5db199SXin Li report = self.write_fake_meta( 420*9c5db199SXin Li '%s.meta' % self._FAKE_TEST_BASENAME, 'fake', payload) 421*9c5db199SXin Li return report 422*9c5db199SXin Li 423*9c5db199SXin Li 424*9c5db199SXin Li def _parse_sender_output(self, output): 425*9c5db199SXin Li """Parse the log output from the crash_sender script. 426*9c5db199SXin Li 427*9c5db199SXin Li This script can run on the logs from either a mocked or true 428*9c5db199SXin Li crash send. It looks for one and only one crash from output. 429*9c5db199SXin Li Non-crash anomalies should be ignored since there're just noise 430*9c5db199SXin Li during running the test. 431*9c5db199SXin Li 432*9c5db199SXin Li @param output: output from the script 433*9c5db199SXin Li 434*9c5db199SXin Li @returns A dictionary with these values: 435*9c5db199SXin Li exec_name: name of executable which crashed 436*9c5db199SXin Li image_type: type of image ("dev","test",...), if given 437*9c5db199SXin Li boot_mode: current boot mode ("dev",...), if given 438*9c5db199SXin Li meta_path: path to the report metadata file 439*9c5db199SXin Li output: the output from the script, copied 440*9c5db199SXin Li report_kind: kind of report sent (minidump vs kernel) 441*9c5db199SXin Li send_attempt: did the script attempt to send a crash. 442*9c5db199SXin Li send_success: if it attempted, was the crash send successful. 443*9c5db199SXin Li sig: signature of the report, if given. 444*9c5db199SXin Li sleep_time: if it attempted, how long did it sleep before 445*9c5db199SXin Li sending (if mocked, how long would it have slept) 446*9c5db199SXin Li """ 447*9c5db199SXin Li anomaly_types = ( 448*9c5db199SXin Li 'kernel_suspend_warning', 449*9c5db199SXin Li 'kernel_warning', 450*9c5db199SXin Li 'kernel_wifi_warning', 451*9c5db199SXin Li 'selinux_violation', 452*9c5db199SXin Li 'service_failure', 453*9c5db199SXin Li ) 454*9c5db199SXin Li 455*9c5db199SXin Li def crash_sender_search(regexp, output): 456*9c5db199SXin Li """Narrow search to lines from crash_sender.""" 457*9c5db199SXin Li return re.search(r'crash_sender\[\d+\]:\s+' + regexp, output) 458*9c5db199SXin Li 459*9c5db199SXin Li before_first_crash = None 460*9c5db199SXin Li while True: 461*9c5db199SXin Li crash_header = crash_sender_search( 462*9c5db199SXin Li 'Considering metadata (\S+)', 463*9c5db199SXin Li output 464*9c5db199SXin Li ) 465*9c5db199SXin Li if not crash_header: 466*9c5db199SXin Li break 467*9c5db199SXin Li if before_first_crash is None: 468*9c5db199SXin Li before_first_crash = output[:crash_header.start()] 469*9c5db199SXin Li meta_considered = crash_header.group(1) 470*9c5db199SXin Li is_anomaly = any(x in meta_considered for x in anomaly_types) 471*9c5db199SXin Li if is_anomaly: 472*9c5db199SXin Li # If it's an anomaly, skip this header, and look for next 473*9c5db199SXin Li # one. 474*9c5db199SXin Li output = output[crash_header.end():] 475*9c5db199SXin Li else: 476*9c5db199SXin Li # If it's not an anomaly, skip everything before this 477*9c5db199SXin Li # header. 478*9c5db199SXin Li output = output[crash_header.start():] 479*9c5db199SXin Li break 480*9c5db199SXin Li if before_first_crash: 481*9c5db199SXin Li output = before_first_crash + output 482*9c5db199SXin Li logging.debug('Filtered sender output to parse:\n%s', output) 483*9c5db199SXin Li 484*9c5db199SXin Li sleep_match = crash_sender_search('Scheduled to send in (\d+)s', output) 485*9c5db199SXin Li send_attempt = sleep_match is not None 486*9c5db199SXin Li if send_attempt: 487*9c5db199SXin Li sleep_time = int(sleep_match.group(1)) 488*9c5db199SXin Li else: 489*9c5db199SXin Li sleep_time = None 490*9c5db199SXin Li 491*9c5db199SXin Li meta_match = crash_sender_search('Metadata: (\S+) \((\S+)\)', output) 492*9c5db199SXin Li if meta_match: 493*9c5db199SXin Li meta_path = meta_match.group(1) 494*9c5db199SXin Li report_kind = meta_match.group(2) 495*9c5db199SXin Li else: 496*9c5db199SXin Li meta_path = None 497*9c5db199SXin Li report_kind = None 498*9c5db199SXin Li 499*9c5db199SXin Li payload_match = crash_sender_search('Payload: (\S+)', output) 500*9c5db199SXin Li if payload_match: 501*9c5db199SXin Li report_payload = payload_match.group(1) 502*9c5db199SXin Li else: 503*9c5db199SXin Li report_payload = None 504*9c5db199SXin Li 505*9c5db199SXin Li exec_name_match = crash_sender_search('Exec name: (\S+)', output) 506*9c5db199SXin Li if exec_name_match: 507*9c5db199SXin Li exec_name = exec_name_match.group(1) 508*9c5db199SXin Li else: 509*9c5db199SXin Li exec_name = None 510*9c5db199SXin Li 511*9c5db199SXin Li sig_match = crash_sender_search('sig: (\S+)', output) 512*9c5db199SXin Li if sig_match: 513*9c5db199SXin Li sig = sig_match.group(1) 514*9c5db199SXin Li else: 515*9c5db199SXin Li sig = None 516*9c5db199SXin Li 517*9c5db199SXin Li image_type_match = crash_sender_search('Image type: (\S+)', output) 518*9c5db199SXin Li if image_type_match: 519*9c5db199SXin Li image_type = image_type_match.group(1) 520*9c5db199SXin Li else: 521*9c5db199SXin Li image_type = None 522*9c5db199SXin Li 523*9c5db199SXin Li boot_mode_match = crash_sender_search('Boot mode: (\S+)', output) 524*9c5db199SXin Li if boot_mode_match: 525*9c5db199SXin Li boot_mode = boot_mode_match.group(1) 526*9c5db199SXin Li else: 527*9c5db199SXin Li boot_mode = None 528*9c5db199SXin Li 529*9c5db199SXin Li send_success = 'Mocking successful send' in output 530*9c5db199SXin Li return {'exec_name': exec_name, 531*9c5db199SXin Li 'report_kind': report_kind, 532*9c5db199SXin Li 'meta_path': meta_path, 533*9c5db199SXin Li 'report_payload': report_payload, 534*9c5db199SXin Li 'send_attempt': send_attempt, 535*9c5db199SXin Li 'send_success': send_success, 536*9c5db199SXin Li 'sig': sig, 537*9c5db199SXin Li 'image_type': image_type, 538*9c5db199SXin Li 'boot_mode': boot_mode, 539*9c5db199SXin Li 'sleep_time': sleep_time, 540*9c5db199SXin Li 'output': output} 541*9c5db199SXin Li 542*9c5db199SXin Li 543*9c5db199SXin Li def wait_for_sender_completion(self): 544*9c5db199SXin Li """Wait for crash_sender to complete. 545*9c5db199SXin Li 546*9c5db199SXin Li Wait for no crash_sender's last message to be placed in the 547*9c5db199SXin Li system log before continuing and for the process to finish. 548*9c5db199SXin Li Otherwise we might get only part of the output.""" 549*9c5db199SXin Li utils.poll_for_condition( 550*9c5db199SXin Li lambda: self._log_reader.can_find('crash_sender done.'), 551*9c5db199SXin Li timeout=60, 552*9c5db199SXin Li exception=error.TestError( 553*9c5db199SXin Li 'Timeout waiting for crash_sender to emit done: ' + 554*9c5db199SXin Li self._log_reader.get_logs())) 555*9c5db199SXin Li utils.poll_for_condition( 556*9c5db199SXin Li lambda: utils.system('pgrep crash_sender', 557*9c5db199SXin Li ignore_status=True) != 0, 558*9c5db199SXin Li timeout=60, 559*9c5db199SXin Li exception=error.TestError( 560*9c5db199SXin Li 'Timeout waiting for crash_sender to finish: ' + 561*9c5db199SXin Li self._log_reader.get_logs())) 562*9c5db199SXin Li 563*9c5db199SXin Li 564*9c5db199SXin Li def _call_sender_one_crash(self, reports_enabled=True, report=None): 565*9c5db199SXin Li """Call the crash sender script to mock upload one crash. 566*9c5db199SXin Li 567*9c5db199SXin Li @param reports_enabled: Has the user consented to sending crash reports. 568*9c5db199SXin Li @param report: report to use for crash, if None we create one. 569*9c5db199SXin Li 570*9c5db199SXin Li @returns a dictionary describing the result with the keys 571*9c5db199SXin Li from _parse_sender_output, as well as: 572*9c5db199SXin Li report_exists: does the minidump still exist after calling 573*9c5db199SXin Li send script 574*9c5db199SXin Li rate_count: how many crashes have been uploaded in the past 575*9c5db199SXin Li 24 hours. 576*9c5db199SXin Li """ 577*9c5db199SXin Li report = self._prepare_sender_one_crash(reports_enabled, 578*9c5db199SXin Li report) 579*9c5db199SXin Li self._log_reader.set_start_by_current() 580*9c5db199SXin Li script_output = "" 581*9c5db199SXin Li try: 582*9c5db199SXin Li script_output = utils.system_output( 583*9c5db199SXin Li '%s --ignore_pause_file 2>&1' % (self._CRASH_SENDER_PATH), 584*9c5db199SXin Li ignore_status=False) 585*9c5db199SXin Li except error.CmdError as err: 586*9c5db199SXin Li raise error.TestFail('"%s" returned an unexpected non-zero ' 587*9c5db199SXin Li 'value (%s).' 588*9c5db199SXin Li % (err.command, err.result_obj.exit_status)) 589*9c5db199SXin Li 590*9c5db199SXin Li self.wait_for_sender_completion() 591*9c5db199SXin Li output = self._log_reader.get_logs() 592*9c5db199SXin Li logging.debug('Crash sender message output:\n %s', output) 593*9c5db199SXin Li 594*9c5db199SXin Li if script_output != '': 595*9c5db199SXin Li logging.debug('crash_sender stdout/stderr: %s', script_output) 596*9c5db199SXin Li 597*9c5db199SXin Li if os.path.exists(report): 598*9c5db199SXin Li report_exists = True 599*9c5db199SXin Li os.remove(report) 600*9c5db199SXin Li else: 601*9c5db199SXin Li report_exists = False 602*9c5db199SXin Li if os.path.exists(self._CRASH_SENDER_RATE_DIR): 603*9c5db199SXin Li rate_count = len([ 604*9c5db199SXin Li name for name in os.listdir(self._CRASH_SENDER_RATE_DIR) 605*9c5db199SXin Li if os.path.isfile(os.path.join(self._CRASH_SENDER_RATE_DIR, 606*9c5db199SXin Li name)) 607*9c5db199SXin Li ]) 608*9c5db199SXin Li else: 609*9c5db199SXin Li rate_count = 0 610*9c5db199SXin Li 611*9c5db199SXin Li result = self._parse_sender_output(output) 612*9c5db199SXin Li result['report_exists'] = report_exists 613*9c5db199SXin Li result['rate_count'] = rate_count 614*9c5db199SXin Li 615*9c5db199SXin Li # Show the result for debugging but remove 'output' key 616*9c5db199SXin Li # since it's large and earlier in debug output. 617*9c5db199SXin Li debug_result = dict(result) 618*9c5db199SXin Li del debug_result['output'] 619*9c5db199SXin Li logging.debug('Result of send (besides output): %s', debug_result) 620*9c5db199SXin Li 621*9c5db199SXin Li return result 622*9c5db199SXin Li 623*9c5db199SXin Li 624*9c5db199SXin Li def enable_crash_filtering(self, name): 625*9c5db199SXin Li """Writes the given parameter to the filter-in file. 626*9c5db199SXin Li 627*9c5db199SXin Li This is used to collect only crashes in which we have an interest. 628*9c5db199SXin Li 629*9c5db199SXin Li @param new_parameter: The filter to write to the file, if any. 630*9c5db199SXin Li """ 631*9c5db199SXin Li utils.open_write_close(self._FILTER_IN, name) 632*9c5db199SXin Li 633*9c5db199SXin Li 634*9c5db199SXin Li def disable_crash_filtering(self): 635*9c5db199SXin Li """Remove the filter-in file. 636*9c5db199SXin Li 637*9c5db199SXin Li Next time the crash reporter is invoked, it will not filter crashes.""" 638*9c5db199SXin Li os.remove(self._FILTER_IN) 639*9c5db199SXin Li 640*9c5db199SXin Li 641*9c5db199SXin Li def initialize(self): 642*9c5db199SXin Li """Initalize the test.""" 643*9c5db199SXin Li test.test.initialize(self) 644*9c5db199SXin Li self._log_reader = cros_logging.make_system_log_reader() 645*9c5db199SXin Li self._leave_crash_sending = True 646*9c5db199SXin Li self._automatic_consent_saving = True 647*9c5db199SXin Li self.enable_crash_filtering('none') 648*9c5db199SXin Li self._set_crash_test_in_progress(True) 649*9c5db199SXin Li 650*9c5db199SXin Li 651*9c5db199SXin Li def cleanup(self): 652*9c5db199SXin Li """Cleanup after the test. 653*9c5db199SXin Li 654*9c5db199SXin Li We reset things back to the way we think they should be. This is 655*9c5db199SXin Li intended to allow the system to continue normal operation. 656*9c5db199SXin Li 657*9c5db199SXin Li Some variables silently change the behavior: 658*9c5db199SXin Li _automatic_consent_saving: if True, we pop the consent file. 659*9c5db199SXin Li _leave_crash_sending: True to enable crash sending, False to 660*9c5db199SXin Li disable it 661*9c5db199SXin Li """ 662*9c5db199SXin Li self._reset_rate_limiting() 663*9c5db199SXin Li self._clear_spooled_crashes() 664*9c5db199SXin Li self._set_system_sending(self._leave_crash_sending) 665*9c5db199SXin Li self._set_sending_mock(mock_enabled=False) 666*9c5db199SXin Li if self._automatic_consent_saving: 667*9c5db199SXin Li self._pop_consent() 668*9c5db199SXin Li self._set_crash_test_in_progress(False) 669*9c5db199SXin Li 670*9c5db199SXin Li # Re-initialize crash reporter to clear any state left over 671*9c5db199SXin Li # (e.g. core_pattern) 672*9c5db199SXin Li self._initialize_crash_reporter(True) 673*9c5db199SXin Li 674*9c5db199SXin Li self.disable_crash_filtering() 675*9c5db199SXin Li 676*9c5db199SXin Li test.test.cleanup(self) 677*9c5db199SXin Li 678*9c5db199SXin Li 679*9c5db199SXin Li def run_crash_tests(self, 680*9c5db199SXin Li test_names, 681*9c5db199SXin Li initialize_crash_reporter=False, 682*9c5db199SXin Li clear_spool_first=True, 683*9c5db199SXin Li must_run_all=True, 684*9c5db199SXin Li lock_core_pattern=False): 685*9c5db199SXin Li """Run crash tests defined in this class. 686*9c5db199SXin Li 687*9c5db199SXin Li @param test_names: Array of test names. 688*9c5db199SXin Li @param initialize_crash_reporter: Should set up crash reporter for every 689*9c5db199SXin Li run. 690*9c5db199SXin Li @param clear_spool_first: Clear all spooled user/system crashes before 691*9c5db199SXin Li starting the test. 692*9c5db199SXin Li @param must_run_all: Should make sure every test in this class is 693*9c5db199SXin Li mentioned in test_names. 694*9c5db199SXin Li @param lock_core_pattern: Lock core_pattern while initializing 695*9c5db199SXin Li crash_reporter. 696*9c5db199SXin Li """ 697*9c5db199SXin Li if self._automatic_consent_saving: 698*9c5db199SXin Li self._push_consent() 699*9c5db199SXin Li 700*9c5db199SXin Li if must_run_all: 701*9c5db199SXin Li # Check test_names is complete 702*9c5db199SXin Li for attr in dir(self): 703*9c5db199SXin Li if attr.find('_test_') == 0: 704*9c5db199SXin Li test_name = attr[6:] 705*9c5db199SXin Li if not test_name in test_names: 706*9c5db199SXin Li raise error.TestError('Test %s is missing' % test_name) 707*9c5db199SXin Li 708*9c5db199SXin Li for test_name in test_names: 709*9c5db199SXin Li logging.info(('=' * 20) + ('Running %s' % test_name) + ('=' * 20)) 710*9c5db199SXin Li if initialize_crash_reporter: 711*9c5db199SXin Li self._initialize_crash_reporter(lock_core_pattern) 712*9c5db199SXin Li # Disable crash_sender from running, kill off any running ones. 713*9c5db199SXin Li # We set a flag to crash_sender when invoking it manually to avoid 714*9c5db199SXin Li # our invocations being paused. 715*9c5db199SXin Li self._set_system_sending(False) 716*9c5db199SXin Li self._kill_running_sender() 717*9c5db199SXin Li self._reset_rate_limiting() 718*9c5db199SXin Li if clear_spool_first: 719*9c5db199SXin Li self._clear_spooled_crashes() 720*9c5db199SXin Li 721*9c5db199SXin Li # Call the test function 722*9c5db199SXin Li getattr(self, '_test_' + test_name)() 723*9c5db199SXin Li 724*9c5db199SXin Li # Clear the intentional crashes, so that the server won't automatically 725*9c5db199SXin Li # report crash as failure. 726*9c5db199SXin Li self._clear_spooled_crashes() 727