• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
dvmDetachSystemThread(Thread * thread)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. */
stateToStatus(char state)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. */
readStateFromBeginning(SystemThread * thread)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  */
readStateRelatively(SystemThread * thread)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 
dvmGetSystemThreadStatus(Thread * thread)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