Home | History | Annotate | Download | only in native
      1 /*
      2  * Copyright (C) 2009 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #define LOG_TAG "SystemThread"
     18 
     19 /*
     20  * System thread support.
     21  */
     22 #include "Dalvik.h"
     23 #include "native/SystemThread.h"
     24 
     25 #include <unistd.h>
     26 #include <sys/types.h>
     27 #include <sys/stat.h>
     28 #include <fcntl.h>
     29 #include <errno.h>
     30 
     31 struct SystemThread {
     32     /*
     33      * /proc/PID/task/TID/stat. -1 if not opened yet. -2 indicates an error
     34      * occurred while opening the file.
     35      */
     36     int statFile;
     37 
     38     /* Offset of state char in stat file, last we checked. */
     39     int stateOffset;
     40 };
     41 
     42 void dvmDetachSystemThread(Thread* thread) {
     43     if (thread->systemThread != NULL) {
     44         if (thread->systemThread->statFile > -1) {
     45             close(thread->systemThread->statFile);
     46         }
     47         free(thread->systemThread);
     48         thread->systemThread = NULL;
     49     }
     50 }
     51 
     52 /* Converts a Linux thread state to a ThreadStatus. */
     53 static ThreadStatus stateToStatus(char state) {
     54     switch (state) {
     55         case 'R': return THREAD_RUNNING;    // running
     56         case 'S': return THREAD_WAIT;       // sleeping in interruptible wait
     57         case 'D': return THREAD_WAIT;       // uninterruptible disk sleep
     58         case 'Z': return THREAD_ZOMBIE;     // zombie
     59         case 'T': return THREAD_WAIT;       // traced or stopped on a signal
     60         case 'W': return THREAD_WAIT;  // paging memory
     61         default:
     62             LOGE("Unexpected state: %c", state);
     63             return THREAD_NATIVE;
     64     }
     65 }
     66 
     67 /* Reads the state char starting from the beginning of the file. */
     68 static char readStateFromBeginning(SystemThread* thread) {
     69     char buffer[256];
     70     int size = read(thread->statFile, buffer, sizeof(buffer) - 1);
     71     if (size <= 0) {
     72         LOGE("read() returned %d: %s", size, strerror(errno));
     73         return 0;
     74     }
     75     char* endOfName = (char*) memchr(buffer, ')', size);
     76     if (endOfName == NULL) {
     77         LOGE("End of executable name not found.");
     78         return 0;
     79     }
     80     char* state = endOfName + 2;
     81     if ((state - buffer) + 1 > size) {
     82         LOGE("Unexpected EOF while trying to read stat file.");
     83         return 0;
     84     }
     85     thread->stateOffset = state - buffer;
     86     return *state;
     87 }
     88 
     89 /*
     90  * Looks for the state char at the last place we found it. Read from the
     91  * beginning if necessary.
     92  */
     93 static char readStateRelatively(SystemThread* thread) {
     94     char buffer[3];
     95     // Position file offset at end of executable name.
     96     int result = lseek(thread->statFile, thread->stateOffset - 2, SEEK_SET);
     97     if (result < 0) {
     98         LOGE("lseek() error.");
     99         return 0;
    100     }
    101     int size = read(thread->statFile, buffer, sizeof(buffer));
    102     if (size < (int) sizeof(buffer)) {
    103         LOGE("Unexpected EOF while trying to read stat file.");
    104         return 0;
    105     }
    106     if (buffer[0] != ')') {
    107         // The executable name must have changed.
    108         result = lseek(thread->statFile, 0, SEEK_SET);
    109         if (result < 0) {
    110             LOGE("lseek() error.");
    111             return 0;
    112         }
    113         return readStateFromBeginning(thread);
    114     }
    115     return buffer[2];
    116 }
    117 
    118 ThreadStatus dvmGetSystemThreadStatus(Thread* thread) {
    119     ThreadStatus status = thread->status;
    120     if (status != THREAD_NATIVE) {
    121         // Return cached status so we don't accidentally return THREAD_NATIVE.
    122         return status;
    123     }
    124 
    125     if (thread->systemThread == NULL) {
    126         thread->systemThread = (SystemThread*) malloc(sizeof(SystemThread));
    127         if (thread->systemThread == NULL) {
    128             LOGE("Couldn't allocate a SystemThread.");
    129             return THREAD_NATIVE;
    130         }
    131         thread->systemThread->statFile = -1;
    132     }
    133 
    134     SystemThread* systemThread = thread->systemThread;
    135     if (systemThread->statFile == -2) {
    136         // We tried and failed to open the file earlier. Return current status.
    137         return thread->status;
    138     }
    139 
    140     // Note: see "man proc" for the format of stat.
    141     // The format is "PID (EXECUTABLE NAME) STATE_CHAR ...".
    142     // Example: "15 (/foo/bar) R ..."
    143     char state;
    144     if (systemThread->statFile == -1) {
    145         // We haven't tried to open the file yet. Do so.
    146         char fileName[256];
    147         sprintf(fileName, "/proc/self/task/%d/stat", thread->systemTid);
    148         systemThread->statFile = open(fileName, O_RDONLY);
    149         if (systemThread->statFile == -1) {
    150             LOGE("Error opening %s: %s", fileName, strerror(errno));
    151             systemThread->statFile = -2;
    152             return thread->status;
    153         }
    154         state = readStateFromBeginning(systemThread);
    155     } else {
    156         state = readStateRelatively(systemThread);
    157     }
    158 
    159     if (state == 0) {
    160         close(systemThread->statFile);
    161         systemThread->statFile = -2;
    162         return thread->status;
    163     }
    164     ThreadStatus nativeStatus = stateToStatus(state);
    165 
    166     // The thread status could have changed from NATIVE.
    167     status = thread->status;
    168     return status == THREAD_NATIVE ? nativeStatus : status;
    169 }
    170