| /* |
| * Copyright (C) 2009 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #define LOG_TAG "SystemThread" |
| |
| /* |
| * System thread support. |
| */ |
| #include "Dalvik.h" |
| #include "native/SystemThread.h" |
| |
| #include <unistd.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| #include <errno.h> |
| |
| struct SystemThread { |
| /* |
| * /proc/PID/task/TID/stat. -1 if not opened yet. -2 indicates an error |
| * occurred while opening the file. |
| */ |
| int statFile; |
| |
| /* Offset of state char in stat file, last we checked. */ |
| int stateOffset; |
| }; |
| |
| void dvmDetachSystemThread(Thread* thread) { |
| if (thread->systemThread != NULL) { |
| if (thread->systemThread->statFile > -1) { |
| close(thread->systemThread->statFile); |
| } |
| free(thread->systemThread); |
| thread->systemThread = NULL; |
| } |
| } |
| |
| /* Converts a Linux thread state to a ThreadStatus. */ |
| static ThreadStatus stateToStatus(char state) { |
| switch (state) { |
| case 'R': return THREAD_RUNNING; // running |
| case 'S': return THREAD_WAIT; // sleeping in interruptible wait |
| case 'D': return THREAD_WAIT; // uninterruptible disk sleep |
| case 'Z': return THREAD_ZOMBIE; // zombie |
| case 'T': return THREAD_WAIT; // traced or stopped on a signal |
| case 'W': return THREAD_WAIT; // paging memory |
| default: |
| LOGE("Unexpected state: %c", state); |
| return THREAD_NATIVE; |
| } |
| } |
| |
| /* Reads the state char starting from the beginning of the file. */ |
| static char readStateFromBeginning(SystemThread* thread) { |
| char buffer[256]; |
| int size = read(thread->statFile, buffer, sizeof(buffer) - 1); |
| if (size <= 0) { |
| LOGE("read() returned %d: %s", size, strerror(errno)); |
| return 0; |
| } |
| char* endOfName = (char*) memchr(buffer, ')', size); |
| if (endOfName == NULL) { |
| LOGE("End of executable name not found."); |
| return 0; |
| } |
| char* state = endOfName + 2; |
| if ((state - buffer) + 1 > size) { |
| LOGE("Unexpected EOF while trying to read stat file."); |
| return 0; |
| } |
| thread->stateOffset = state - buffer; |
| return *state; |
| } |
| |
| /* |
| * Looks for the state char at the last place we found it. Read from the |
| * beginning if necessary. |
| */ |
| static char readStateRelatively(SystemThread* thread) { |
| char buffer[3]; |
| // Position file offset at end of executable name. |
| int result = lseek(thread->statFile, thread->stateOffset - 2, SEEK_SET); |
| if (result < 0) { |
| LOGE("lseek() error."); |
| return 0; |
| } |
| int size = read(thread->statFile, buffer, sizeof(buffer)); |
| if (size < (int) sizeof(buffer)) { |
| LOGE("Unexpected EOF while trying to read stat file."); |
| return 0; |
| } |
| if (buffer[0] != ')') { |
| // The executable name must have changed. |
| result = lseek(thread->statFile, 0, SEEK_SET); |
| if (result < 0) { |
| LOGE("lseek() error."); |
| return 0; |
| } |
| return readStateFromBeginning(thread); |
| } |
| return buffer[2]; |
| } |
| |
| ThreadStatus dvmGetSystemThreadStatus(Thread* thread) { |
| ThreadStatus status = thread->status; |
| if (status != THREAD_NATIVE) { |
| // Return cached status so we don't accidentally return THREAD_NATIVE. |
| return status; |
| } |
| |
| if (thread->systemThread == NULL) { |
| thread->systemThread = (SystemThread*) malloc(sizeof(SystemThread)); |
| if (thread->systemThread == NULL) { |
| LOGE("Couldn't allocate a SystemThread."); |
| return THREAD_NATIVE; |
| } |
| thread->systemThread->statFile = -1; |
| } |
| |
| SystemThread* systemThread = thread->systemThread; |
| if (systemThread->statFile == -2) { |
| // We tried and failed to open the file earlier. Return current status. |
| return thread->status; |
| } |
| |
| // Note: see "man proc" for the format of stat. |
| // The format is "PID (EXECUTABLE NAME) STATE_CHAR ...". |
| // Example: "15 (/foo/bar) R ..." |
| char state; |
| if (systemThread->statFile == -1) { |
| // We haven't tried to open the file yet. Do so. |
| char fileName[256]; |
| sprintf(fileName, "/proc/self/task/%d/stat", thread->systemTid); |
| systemThread->statFile = open(fileName, O_RDONLY); |
| if (systemThread->statFile == -1) { |
| LOGE("Error opening %s: %s", fileName, strerror(errno)); |
| systemThread->statFile = -2; |
| return thread->status; |
| } |
| state = readStateFromBeginning(systemThread); |
| } else { |
| state = readStateRelatively(systemThread); |
| } |
| |
| if (state == 0) { |
| close(systemThread->statFile); |
| systemThread->statFile = -2; |
| return thread->status; |
| } |
| ThreadStatus nativeStatus = stateToStatus(state); |
| |
| // The thread status could have changed from NATIVE. |
| status = thread->status; |
| return status == THREAD_NATIVE ? nativeStatus : status; |
| } |