From 7c5b6d11532b2904c19fccaacd709b79b80b4f20 Mon Sep 17 00:00:00 2001 From: Jing Ji Date: Fri, 12 Jul 2024 14:52:13 -0700 Subject: Keep reading until end-of-file in reading procfs As the pread(2) says "it is not an error for a successful call to transfer fewer bytes than requested". The only way to verify we're hitting the end-of-file is to keep reading from the file until it returns 0. Here we're following this rule, in order to make sure we read the whole procfs file to avoid the parser errors. The drawback is that, now for every procfs read, it'll have to hit the pread() at least twice. But if it has reached to the end-of-file, the last read should return within microseconds. Bug: 351917521 Flag: EXEMPT bugfix Test: dumpsys cpuinfo Change-Id: If6a14c4e8f4f1821628f1efb984f8064d4969a8c Merged-In: If6a14c4e8f4f1821628f1efb984f8064d4969a8c --- core/jni/android_util_Process.cpp | 52 +++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp index d2e58bb62c46..d7a0b0408e99 100644 --- a/core/jni/android_util_Process.cpp +++ b/core/jni/android_util_Process.cpp @@ -1063,8 +1063,8 @@ jboolean android_os_Process_readProcFile(JNIEnv* env, jobject clazz, } env->ReleaseStringUTFChars(file, file8); - // Most proc files we read are small, so we only go through the - // loop once and use the stack buffer. We allocate a buffer big + // Most proc files we read are small, so we go through the loop + // with the stack buffer firstly. We allocate a buffer big // enough for the whole file. char readBufferStack[kProcReadStackBufferSize]; @@ -1072,37 +1072,47 @@ jboolean android_os_Process_readProcFile(JNIEnv* env, jobject clazz, char* readBuffer = &readBufferStack[0]; ssize_t readBufferSize = kProcReadStackBufferSize; ssize_t numberBytesRead; + off_t offset = 0; for (;;) { + ssize_t requestedBufferSize = readBufferSize - offset; // By using pread, we can avoid an lseek to rewind the FD // before retry, saving a system call. - numberBytesRead = pread(fd, readBuffer, readBufferSize, 0); - if (numberBytesRead < 0 && errno == EINTR) { - continue; - } + numberBytesRead = + TEMP_FAILURE_RETRY(pread(fd, readBuffer + offset, requestedBufferSize, offset)); if (numberBytesRead < 0) { if (kDebugProc) { - ALOGW("Unable to open process file: %s fd=%d\n", file8, fd.get()); + ALOGW("Unable to read process file err: %s file: %s fd=%d\n", + strerror_r(errno, &readBufferStack[0], sizeof(readBufferStack)), file8, + fd.get()); } return JNI_FALSE; } - if (numberBytesRead < readBufferSize) { + if (numberBytesRead == 0) { + // End of file. + numberBytesRead = offset; break; } - if (readBufferSize > std::numeric_limits::max() / 2) { - if (kDebugProc) { - ALOGW("Proc file too big: %s fd=%d\n", file8, fd.get()); + if (numberBytesRead < requestedBufferSize) { + // Read less bytes than requested, it's not an error per pread(2). + offset += numberBytesRead; + } else { + // Buffer is fully used, try to grow it. + if (readBufferSize > std::numeric_limits::max() / 2) { + if (kDebugProc) { + ALOGW("Proc file too big: %s fd=%d\n", file8, fd.get()); + } + return JNI_FALSE; } - return JNI_FALSE; - } - readBufferSize = std::max(readBufferSize * 2, - kProcReadMinHeapBufferSize); - readBufferHeap.reset(); // Free address space before getting more. - readBufferHeap = std::make_unique(readBufferSize); - if (!readBufferHeap) { - jniThrowException(env, "java/lang/OutOfMemoryError", NULL); - return JNI_FALSE; + readBufferSize = std::max(readBufferSize * 2, kProcReadMinHeapBufferSize); + readBufferHeap.reset(); // Free address space before getting more. + readBufferHeap = std::make_unique(readBufferSize); + if (!readBufferHeap) { + jniThrowException(env, "java/lang/OutOfMemoryError", NULL); + return JNI_FALSE; + } + readBuffer = readBufferHeap.get(); + offset = 0; } - readBuffer = readBufferHeap.get(); } // parseProcLineArray below modifies the buffer while parsing! -- cgit v1.2.3-59-g8ed1b