in tools/android/emulator/emulated_device.py [0:0]
def StoreAndCompressUserdata(self, location, ram_binary_location=None):
"""Stores the emulator's userdata files."""
assert not self._running, 'Emulator is still running.'
assert self._images_dir, 'Emulator never started.'
assert not self._child_will_delete_tmp, 'Emulator is deleting tmp dir.'
if not os.path.exists(os.path.dirname(location)):
os.makedirs(os.path.dirname(location))
logging.info('Storing emulator state to: %s', location)
image_files = [
self._UserdataQemuFile() + self._PossibleImgSuffix(),
self._CacheFile() + self._PossibleImgSuffix(),
self._SdcardFile() + self._PossibleImgSuffix(),
self._SnapshotFile(),
self._RamdiskFile()]
encryption_qcow = self._EncryptionKeyImageFile() + self._PossibleImgSuffix()
if os.path.exists(encryption_qcow):
image_files.append(encryption_qcow)
if os.path.exists(self._VendorFile()):
image_files.append(self._VendorFile() + self._PossibleImgSuffix())
if self.GetApiVersion() >= 28:
image_files.append(self._UserdataQemuFile())
if (self._metadata_pb.emulator_type ==
emulator_meta_data_pb2.EmulatorMetaDataPb.QEMU2):
image_files.append(
os.path.join(self._SessionImagesDir(), 'version_num.cache'))
image_files.append(self._SystemFile() + self._PossibleImgSuffix())
# The snapshots directory if it is successfully generated is of the
# following directory structure
# |- snapshots
# | |- default_boot
# | | |- hardware.ini
# | | |- ram.bin
# | | |- snapshot.pb
# | | |- textures.bin
# | |- snapshot_deps.pb
# Of all the files the snapshot feature creates, ram.bin is the file that's
# the largest and instead of wrapping up ram.bin within the userdata.tar
# file, we explicitly emit out a separate file so that we don't waste
# cycles in untarring the huge file and using tmpfs space during the start
# cycle. ram.bin is symlinked to the file path that are passed as
# inputs. So all the other files are tarred up in the userdata.tar.gz file
# and ram.bin is explicitly copied over as a output file.
snapshot_file_found = False
for r, _, f in os.walk(os.path.join(self._SessionImagesDir(), 'snapshots')):
for each_file in f:
if each_file == 'ram.bin' and ram_binary_location:
shutil.copy(os.path.join(r, each_file), ram_binary_location)
snapshot_file_found = True
continue
image_files.append(os.path.join(r, each_file))
# TODO: Instead of failing and causing BUILD failures, I think we
# should probably return back a empty file and subsequent loads would just
# do regular boots since they don't get build failures. We should record
# it and find out how often that happens though.
if ram_binary_location and not snapshot_file_found:
raise Exception('Requested to save snapshots but didnt find ram.bin')
# Before compressing make sure none of the files are being modified since
# the Kill command is not really synchronous. The best way to deal with that
# sitation is either wait for the Emulator process to die and since we use
# os.fork, we might have to pass around the emulator pid back to the parent
# process. The easiest thing to do would be to lsof all the files that we
# are tarring and that'll guarantee that no other process is writing to
# those files before we are really shutdown.
# Hopefully with v2 design, we don't have to fork.
all_files_closed = False
lsof_command = ['/usr/bin/lsof'] + image_files
# Wait for 10 seconds before giving up.
for _ in range(10):
try:
output = subprocess.check_output(lsof_command)
logging.info('lsof output :%s', output)
except subprocess.CalledProcessError as err:
# If no processes are writing to it, then we are done and it will throw
# a exception.
if err.returncode == 1:
all_files_closed = True
break
time.sleep(1)
image_files = ['./%s' % os.path.relpath(f, self._images_dir)
for f in image_files]
if not all_files_closed:
raise Exception('Emulator still not dead after issuing KILL and waiting '
'10 seconds')
if (self._metadata_pb.emulator_type ==
emulator_meta_data_pb2.EmulatorMetaDataPb.QEMU2):
# QEMU2 uses .qcow disk images which are diffs over the original
# userdata.img. Therefore they're already quite small and the
# format will not compress very well. (roughly 2x vs RAW images which
# compress 4x or better).
# so running thru gzip is slow and doesn't save much space.
subprocess.check_call([
'tar',
'-cSpf',
location,
'-C',
self._images_dir] + image_files)
logging.info('Tar/gz pipeline completes.')
else:
with open(location, 'w') as dat_file:
tar_proc = subprocess.Popen(
['tar', '-cSp', '-C', self._images_dir] + image_files,
stdout=subprocess.PIPE)
# consider replacing with zippy?
gz_proc = subprocess.Popen(
['gzip'],
stdin=tar_proc.stdout,
stdout=dat_file)
tar_proc.stdout.close() # tar will get a SIGPIPE if gz dies.
gz_ret = gz_proc.wait()
tar_ret = tar_proc.wait()
assert gz_ret == 0 and tar_ret == 0, 'gz: %d tar: %d' % (gz_ret,
tar_ret)
logging.info('Tar/gz pipeline completes.')