View Javadoc
1   /*
2    * Copyright 2020-2023 The OSHI Project Contributors
3    * SPDX-License-Identifier: MIT
4    */
5   package oshi.software.os.linux;
6   
7   import java.util.Locale;
8   import java.util.Map;
9   
10  import oshi.annotation.concurrent.ThreadSafe;
11  import oshi.driver.linux.proc.ProcessStat;
12  import oshi.software.common.AbstractOSThread;
13  import oshi.software.os.OSProcess.State;
14  import oshi.util.FileUtil;
15  import oshi.util.ParseUtil;
16  import oshi.util.platform.linux.ProcPath;
17  
18  /**
19   * OSThread implementation
20   */
21  @ThreadSafe
22  public class LinuxOSThread extends AbstractOSThread {
23  
24      private static final int[] PROC_TASK_STAT_ORDERS = new int[LinuxOSThread.ThreadPidStat.values().length];
25      static {
26          for (LinuxOSThread.ThreadPidStat stat : LinuxOSThread.ThreadPidStat.values()) {
27              // The PROC_PID_STAT enum indices are 1-indexed.
28              // Subtract one to get a zero-based index
29              PROC_TASK_STAT_ORDERS[stat.ordinal()] = stat.getOrder() - 1;
30          }
31      }
32  
33      private final int threadId;
34      private String name;
35      private State state = State.INVALID;
36      private long minorFaults;
37      private long majorFaults;
38      private long startMemoryAddress;
39      private long contextSwitches;
40      private long kernelTime;
41      private long userTime;
42      private long startTime;
43      private long upTime;
44      private int priority;
45  
46      public LinuxOSThread(int processId, int tid) {
47          super(processId);
48          this.threadId = tid;
49          updateAttributes();
50      }
51  
52      @Override
53      public int getThreadId() {
54          return this.threadId;
55      }
56  
57      @Override
58      public String getName() {
59          return this.name;
60      }
61  
62      @Override
63      public State getState() {
64          return this.state;
65      }
66  
67      @Override
68      public long getStartTime() {
69          return this.startTime;
70      }
71  
72      @Override
73      public long getStartMemoryAddress() {
74          return this.startMemoryAddress;
75      }
76  
77      @Override
78      public long getContextSwitches() {
79          return this.contextSwitches;
80      }
81  
82      @Override
83      public long getMinorFaults() {
84          return this.minorFaults;
85      }
86  
87      @Override
88      public long getMajorFaults() {
89          return this.majorFaults;
90      }
91  
92      @Override
93      public long getKernelTime() {
94          return this.kernelTime;
95      }
96  
97      @Override
98      public long getUserTime() {
99          return this.userTime;
100     }
101 
102     @Override
103     public long getUpTime() {
104         return this.upTime;
105     }
106 
107     @Override
108     public int getPriority() {
109         return this.priority;
110     }
111 
112     @Override
113     public boolean updateAttributes() {
114         this.name = FileUtil.getStringFromFile(
115                 String.format(Locale.ROOT, ProcPath.TASK_COMM, this.getOwningProcessId(), this.threadId));
116         Map<String, String> status = FileUtil.getKeyValueMapFromFile(
117                 String.format(Locale.ROOT, ProcPath.TASK_STATUS, this.getOwningProcessId(), this.threadId), ":");
118         String stat = FileUtil.getStringFromFile(
119                 String.format(Locale.ROOT, ProcPath.TASK_STAT, this.getOwningProcessId(), this.threadId));
120         if (stat.isEmpty()) {
121             this.state = State.INVALID;
122             return false;
123         }
124         long now = System.currentTimeMillis();
125         long[] statArray = ParseUtil.parseStringToLongArray(stat, PROC_TASK_STAT_ORDERS,
126                 ProcessStat.PROC_PID_STAT_LENGTH, ' ');
127 
128         // BOOTTIME is in seconds and start time from proc/pid/stat is in jiffies.
129         // Combine units to jiffies and convert to millijiffies before hz division to
130         // avoid precision loss without having to cast
131         this.startTime = (LinuxOperatingSystem.BOOTTIME * LinuxOperatingSystem.getHz()
132                 + statArray[LinuxOSThread.ThreadPidStat.START_TIME.ordinal()]) * 1000L / LinuxOperatingSystem.getHz();
133         // BOOT_TIME could be up to 500ms off and start time up to 5ms off. A process
134         // that has started within last 505ms could produce a future start time/negative
135         // up time, so insert a sanity check.
136         if (this.startTime >= now) {
137             this.startTime = now - 1;
138         }
139         this.minorFaults = statArray[ThreadPidStat.MINOR_FAULTS.ordinal()];
140         this.majorFaults = statArray[ThreadPidStat.MAJOR_FAULT.ordinal()];
141         this.startMemoryAddress = statArray[ThreadPidStat.START_CODE.ordinal()];
142         long voluntaryContextSwitches = ParseUtil.parseLongOrDefault(status.get("voluntary_ctxt_switches"), 0L);
143         long nonVoluntaryContextSwitches = ParseUtil.parseLongOrDefault(status.get("nonvoluntary_ctxt_switches"), 0L);
144         this.contextSwitches = voluntaryContextSwitches + nonVoluntaryContextSwitches;
145         this.state = ProcessStat.getState(status.getOrDefault("State", "U").charAt(0));
146         this.kernelTime = statArray[ThreadPidStat.KERNEL_TIME.ordinal()] * 1000L / LinuxOperatingSystem.getHz();
147         this.userTime = statArray[ThreadPidStat.USER_TIME.ordinal()] * 1000L / LinuxOperatingSystem.getHz();
148         this.upTime = now - startTime;
149         this.priority = (int) statArray[ThreadPidStat.PRIORITY.ordinal()];
150         return true;
151     }
152 
153     /**
154      * Enum used to update attributes. The order field represents the 1-indexed numeric order of the stat in
155      * /proc/pid/task/tid/stat per the man file.
156      */
157     private enum ThreadPidStat {
158         // The parsing implementation in ParseUtil requires these to be declared
159         // in increasing order
160         PPID(4), MINOR_FAULTS(10), MAJOR_FAULT(12), USER_TIME(14), KERNEL_TIME(15), PRIORITY(18), THREAD_COUNT(20),
161         START_TIME(22), VSZ(23), RSS(24), START_CODE(26);
162 
163         private final int order;
164 
165         ThreadPidStat(int order) {
166             this.order = order;
167         }
168 
169         public int getOrder() {
170             return this.order;
171         }
172     }
173 }