Merge "Match maximum command line size for startup tracing."
diff --git a/libc/bionic/jemalloc_wrapper.cpp b/libc/bionic/jemalloc_wrapper.cpp
index fd99814..bf0494d 100644
--- a/libc/bionic/jemalloc_wrapper.cpp
+++ b/libc/bionic/jemalloc_wrapper.cpp
@@ -128,10 +128,10 @@
 
 __BEGIN_DECLS
 
-size_t __mallinfo_narenas();
-size_t __mallinfo_nbins();
-struct mallinfo __mallinfo_arena_info(size_t);
-struct mallinfo __mallinfo_bin_info(size_t, size_t);
+size_t je_mallinfo_narenas();
+size_t je_mallinfo_nbins();
+struct mallinfo je_mallinfo_arena_info(size_t);
+struct mallinfo je_mallinfo_bin_info(size_t, size_t);
 
 __END_DECLS
 
@@ -144,8 +144,8 @@
   MallocXmlElem root(fp, "malloc", "version=\"jemalloc-1\"");
 
   // Dump all of the large allocations in the arenas.
-  for (size_t i = 0; i < __mallinfo_narenas(); i++) {
-    struct mallinfo mi = __mallinfo_arena_info(i);
+  for (size_t i = 0; i < je_mallinfo_narenas(); i++) {
+    struct mallinfo mi = je_mallinfo_arena_info(i);
     if (mi.hblkhd != 0) {
       MallocXmlElem arena_elem(fp, "heap", "nr=\"%d\"", i);
       {
@@ -154,8 +154,8 @@
         MallocXmlElem(fp, "allocated-bins").Contents("%zu", mi.fsmblks);
 
         size_t total = 0;
-        for (size_t j = 0; j < __mallinfo_nbins(); j++) {
-          struct mallinfo mi = __mallinfo_bin_info(i, j);
+        for (size_t j = 0; j < je_mallinfo_nbins(); j++) {
+          struct mallinfo mi = je_mallinfo_bin_info(i, j);
           if (mi.ordblks != 0) {
             MallocXmlElem bin_elem(fp, "bin", "nr=\"%d\"", j);
             MallocXmlElem(fp, "allocated").Contents("%zu", mi.ordblks);
diff --git a/libc/stdio/local.h b/libc/stdio/local.h
index d306a21..1ecf122 100644
--- a/libc/stdio/local.h
+++ b/libc/stdio/local.h
@@ -204,6 +204,7 @@
 __LIBC32_LEGACY_PUBLIC__ int _fwalk(int (*)(FILE*));
 
 off64_t __sseek64(void*, off64_t, int);
+int __sflush_locked(FILE*);
 int __swhatbuf(FILE*, size_t*, int*);
 wint_t __fgetwc_unlock(FILE*);
 wint_t __ungetwc(wint_t, FILE*);
diff --git a/libc/stdio/refill.c b/libc/stdio/refill.c
index cfa2bfd..1df4191 100644
--- a/libc/stdio/refill.c
+++ b/libc/stdio/refill.c
@@ -40,7 +40,7 @@
 lflush(FILE *fp)
 {
 	if ((fp->_flags & (__SLBF|__SWR)) == (__SLBF|__SWR))
-		return (__sflush(fp));	/* ignored... */
+		return (__sflush_locked(fp));	/* ignored... */
 	return (0);
 }
 
diff --git a/libc/stdio/stdio.cpp b/libc/stdio/stdio.cpp
index 4cec757..91c7689 100644
--- a/libc/stdio/stdio.cpp
+++ b/libc/stdio/stdio.cpp
@@ -106,7 +106,7 @@
 FILE* stdout = &__sF[1];
 FILE* stderr = &__sF[2];
 
-static pthread_mutex_t __stdio_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
+static pthread_mutex_t __stdio_mutex = PTHREAD_MUTEX_INITIALIZER;
 
 static uint64_t __get_file_tag(FILE* fp) {
   // Don't use a tag for the standard streams.
@@ -211,21 +211,23 @@
 }
 
 int _fwalk(int (*callback)(FILE*)) {
-  pthread_mutex_lock(&__stdio_mutex);
   int result = 0;
   for (glue* g = &__sglue; g != nullptr; g = g->next) {
     FILE* fp = g->iobs;
     for (int n = g->niobs; --n >= 0; ++fp) {
-      ScopedFileLock sfl(fp);
       if (fp->_flags != 0 && (fp->_flags & __SIGN) == 0) {
         result |= (*callback)(fp);
       }
     }
   }
-  pthread_mutex_unlock(&__stdio_mutex);
   return result;
 }
 
+extern "C" __LIBC_HIDDEN__ void __libc_stdio_cleanup(void) {
+  // Equivalent to fflush(nullptr), but without all the locking since we're shutting down anyway.
+  _fwalk(__sflush);
+}
+
 static FILE* __fopen(int fd, int flags) {
 #if !defined(__LP64__)
   if (fd > SHRT_MAX) {
@@ -520,6 +522,11 @@
   return 0;
 }
 
+int __sflush_locked(FILE* fp) {
+  ScopedFileLock sfl(fp);
+  return __sflush(fp);
+}
+
 int __sread(void* cookie, char* buf, int n) {
   FILE* fp = reinterpret_cast<FILE*>(cookie);
   return TEMP_FAILURE_RETRY(read(fp->_file, buf, n));
@@ -1061,7 +1068,7 @@
 }
 
 static int fflush_all() {
-  return _fwalk(__sflush);
+  return _fwalk(__sflush_locked);
 }
 
 int fflush(FILE* fp) {
diff --git a/libc/stdlib/atexit.c b/libc/stdlib/atexit.c
index 692e0c0..0efb118 100644
--- a/libc/stdlib/atexit.c
+++ b/libc/stdlib/atexit.c
@@ -188,7 +188,8 @@
 
 	/* If called via exit(), flush output of all open files. */
 	if (dso == NULL) {
-		fflush(NULL);
+		extern void __libc_stdio_cleanup(void);
+		__libc_stdio_cleanup();
 	}
 
   /* BEGIN android-changed: call __unregister_atfork if dso is not null */
diff --git a/libdl/Android.bp b/libdl/Android.bp
index b1ee5ab..fbbe7ba 100644
--- a/libdl/Android.bp
+++ b/libdl/Android.bp
@@ -47,6 +47,7 @@
 
     ldflags: [
         "-Wl,--exclude-libs=libgcc.a",
+        "-Wl,--exclude-libs=libgcc_stripped.a",
         "-Wl,--exclude-libs=libclang_rt.builtins-arm-android.a",
         "-Wl,--exclude-libs=libclang_rt.builtins-aarch64-android.a",
         "-Wl,--exclude-libs=libclang_rt.builtins-x86-android.a",
@@ -127,6 +128,7 @@
 
     ldflags: [
         "-Wl,--exclude-libs=libgcc.a",
+        "-Wl,--exclude-libs=libgcc_stripped.a",
         "-Wl,--exclude-libs=libclang_rt.builtins-arm-android.a",
         "-Wl,--exclude-libs=libclang_rt.builtins-aarch64-android.a",
         "-Wl,--exclude-libs=libclang_rt.builtins-x86-android.a",
diff --git a/linker/Android.bp b/linker/Android.bp
index 95fd4f7..e908678 100644
--- a/linker/Android.bp
+++ b/linker/Android.bp
@@ -318,6 +318,7 @@
 
     ldflags: [
         "-Wl,--exclude-libs=libgcc.a",
+        "-Wl,--exclude-libs=libgcc_stripped.a",
         "-Wl,--exclude-libs=libclang_rt.builtins-arm-android.a",
         "-Wl,--exclude-libs=libclang_rt.builtins-aarch64-android.a",
         "-Wl,--exclude-libs=libclang_rt.builtins-x86-android.a",
diff --git a/linker/linker.cpp b/linker/linker.cpp
index b59df73..0de17f7 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -1039,7 +1039,7 @@
 
   ZipEntry entry;
 
-  if (FindEntry(handle, ZipString(file_path), &entry) != 0) {
+  if (FindEntry(handle, file_path, &entry) != 0) {
     // Entry was not found.
     close(fd);
     return -1;
diff --git a/tests/dlext_test.cpp b/tests/dlext_test.cpp
index eed84a4..67ebf37 100644
--- a/tests/dlext_test.cpp
+++ b/tests/dlext_test.cpp
@@ -134,10 +134,7 @@
   ZipArchiveHandle handle;
   ASSERT_EQ(0, OpenArchive(lib_path.c_str(), &handle));
   ZipEntry zip_entry;
-  ZipString zip_name;
-  zip_name.name = reinterpret_cast<const uint8_t*>(kLibZipSimpleZip);
-  zip_name.name_length = strlen(kLibZipSimpleZip);
-  ASSERT_EQ(0, FindEntry(handle, zip_name, &zip_entry));
+  ASSERT_EQ(0, FindEntry(handle, kLibZipSimpleZip, &zip_entry));
   extinfo.library_fd_offset = zip_entry.offset;
   CloseArchive(handle);
 
diff --git a/tests/malloc_test.cpp b/tests/malloc_test.cpp
index 706de15..983592f 100644
--- a/tests/malloc_test.cpp
+++ b/tests/malloc_test.cpp
@@ -395,6 +395,60 @@
 #endif
 }
 
+TEST(malloc, malloc_info_matches_mallinfo) {
+#ifdef __BIONIC__
+  SKIP_WITH_HWASAN; // hwasan does not implement malloc_info
+
+  char* buf;
+  size_t bufsize;
+  FILE* memstream = open_memstream(&buf, &bufsize);
+  ASSERT_NE(nullptr, memstream);
+  size_t mallinfo_before_allocated_bytes = mallinfo().uordblks;
+  ASSERT_EQ(0, malloc_info(0, memstream));
+  size_t mallinfo_after_allocated_bytes = mallinfo().uordblks;
+  ASSERT_EQ(0, fclose(memstream));
+
+  tinyxml2::XMLDocument doc;
+  ASSERT_EQ(tinyxml2::XML_SUCCESS, doc.Parse(buf));
+
+  size_t total_allocated_bytes = 0;
+  auto root = doc.FirstChildElement();
+  ASSERT_NE(nullptr, root);
+  ASSERT_STREQ("malloc", root->Name());
+  if (std::string(root->Attribute("version")) == "jemalloc-1") {
+    // Verify jemalloc version of this data.
+    ASSERT_STREQ("jemalloc-1", root->Attribute("version"));
+
+    auto arena = root->FirstChildElement();
+    for (; arena != nullptr; arena = arena->NextSiblingElement()) {
+      int val;
+
+      ASSERT_STREQ("heap", arena->Name());
+      ASSERT_EQ(tinyxml2::XML_SUCCESS, arena->QueryIntAttribute("nr", &val));
+      ASSERT_EQ(tinyxml2::XML_SUCCESS,
+                arena->FirstChildElement("allocated-large")->QueryIntText(&val));
+      total_allocated_bytes += val;
+      ASSERT_EQ(tinyxml2::XML_SUCCESS,
+                arena->FirstChildElement("allocated-huge")->QueryIntText(&val));
+      total_allocated_bytes += val;
+      ASSERT_EQ(tinyxml2::XML_SUCCESS,
+                arena->FirstChildElement("allocated-bins")->QueryIntText(&val));
+      total_allocated_bytes += val;
+      ASSERT_EQ(tinyxml2::XML_SUCCESS,
+                arena->FirstChildElement("bins-total")->QueryIntText(&val));
+    }
+    // The total needs to be between the mallinfo call before and after
+    // since malloc_info allocates some memory.
+    EXPECT_LE(mallinfo_before_allocated_bytes, total_allocated_bytes);
+    EXPECT_GE(mallinfo_after_allocated_bytes, total_allocated_bytes);
+  } else {
+    // Only verify that this is debug-malloc-1, the malloc debug unit tests
+    // verify the output.
+    ASSERT_STREQ("debug-malloc-1", root->Attribute("version"));
+  }
+#endif
+}
+
 TEST(malloc, calloc_usable_size) {
   for (size_t size = 1; size <= 2048; size++) {
     void* pointer = malloc(size);
diff --git a/tests/stdio_test.cpp b/tests/stdio_test.cpp
index 65a942c..c237d6d 100644
--- a/tests/stdio_test.cpp
+++ b/tests/stdio_test.cpp
@@ -29,6 +29,7 @@
 #include <locale.h>
 
 #include <string>
+#include <thread>
 #include <vector>
 
 #include <android-base/file.h>
@@ -2577,3 +2578,24 @@
   ASSERT_LT(0, length);
   ASSERT_EQ("/proc/self/fd/2", std::string(path, length));
 }
+
+TEST(STDIO_TEST, fread_with_locked_file) {
+  // Reading an unbuffered/line-buffered file from one thread shouldn't block on
+  // files locked on other threads, even if it flushes some line-buffered files.
+  FILE* fp1 = fopen("/dev/zero", "r");
+  ASSERT_TRUE(fp1 != nullptr);
+  flockfile(fp1);
+
+  std::thread([] {
+    for (int mode : { _IONBF, _IOLBF }) {
+      FILE* fp2 = fopen("/dev/zero", "r");
+      ASSERT_TRUE(fp2 != nullptr);
+      setvbuf(fp2, nullptr, mode, 0);
+      ASSERT_EQ('\0', fgetc(fp2));
+      fclose(fp2);
+    }
+  }).join();
+
+  funlockfile(fp1);
+  fclose(fp1);
+}