Implement a simple count based boot marker.

We write the number of failed boots to the marker and only
prune the dalvik cache if the number of consecutive failed
boots is higher than a predefined threshold.

Note that the code is forgiving of errors related to boot
markers; we continue the boot process even if we're unable
to create / write or flush the boot marker.

bug: 19360096
Change-Id: Ia17c3b783318ddf43c9199d0f7f09c54a4176667
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 261d3c2..d873e6d 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -19,6 +19,7 @@
 #include <dirent.h>
 #include <sys/statvfs.h>
 #include <sys/types.h>
+#include <unistd.h>
 
 #include <random>
 
@@ -122,21 +123,50 @@
 // every zygote boot and delete it when the boot completes. If we find a file already
 // present, it usually means the boot didn't complete. We wipe the entire dalvik
 // cache if that's the case.
-static void MarkZygoteStart(const InstructionSet isa) {
+static void MarkZygoteStart(const InstructionSet isa, const uint32_t max_failed_boots) {
   const std::string isa_subdir = GetDalvikCacheOrDie(GetInstructionSetString(isa), false);
   const std::string boot_marker = isa_subdir + "/.booting";
+  const char* file_name = boot_marker.c_str();
 
-  if (OS::FileExists(boot_marker.c_str())) {
+  uint32_t num_failed_boots = 0;
+  std::unique_ptr<File> file(OS::OpenFileReadWrite(file_name));
+  if (file.get() == nullptr) {
+    file.reset(OS::CreateEmptyFile(file_name));
+
+    if (file.get() == nullptr) {
+      PLOG(WARNING) << "Failed to create boot marker.";
+      return;
+    }
+  } else {
+    if (!file->ReadFully(&num_failed_boots, sizeof(num_failed_boots))) {
+      PLOG(WARNING) << "Failed to read boot marker.";
+      file->Erase();
+      return;
+    }
+  }
+
+  if (max_failed_boots != 0 && num_failed_boots > max_failed_boots) {
     LOG(WARNING) << "Incomplete boot detected. Pruning dalvik cache";
     RealPruneDalvikCache(isa_subdir);
   }
 
-  VLOG(startup) << "Creating boot start marker: " << boot_marker;
-  std::unique_ptr<File> f(OS::CreateEmptyFile(boot_marker.c_str()));
-  if (f.get() != nullptr) {
-    if (f->FlushCloseOrErase() != 0) {
-      PLOG(WARNING) << "Failed to write boot marker.";
-    }
+  ++num_failed_boots;
+  VLOG(startup) << "Number of failed boots on : " << boot_marker << " = " << num_failed_boots;
+
+  if (lseek(file->Fd(), 0, SEEK_SET) == -1) {
+    PLOG(WARNING) << "Failed to write boot marker.";
+    file->Erase();
+    return;
+  }
+
+  if (!file->WriteFully(&num_failed_boots, sizeof(num_failed_boots))) {
+    PLOG(WARNING) << "Failed to write boot marker.";
+    file->Erase();
+    return;
+  }
+
+  if (file->FlushCloseOrErase() != 0) {
+    PLOG(WARNING) << "Failed to flush boot marker.";
   }
 }
 
@@ -450,7 +480,7 @@
                                              &has_cache, &is_global_cache);
 
   if (Runtime::Current()->IsZygote()) {
-    MarkZygoteStart(image_isa);
+    MarkZygoteStart(image_isa, Runtime::Current()->GetZygoteMaxFailedBoots());
   }
 
   ImageSpace* space;