1
2
3
4
5 package oshi.driver.unix.solaris;
6
7 import java.util.ArrayList;
8 import java.util.LinkedHashMap;
9 import java.util.List;
10 import java.util.Locale;
11 import java.util.Map;
12
13 import org.slf4j.Logger;
14 import org.slf4j.LoggerFactory;
15
16 import com.sun.jna.Memory;
17 import com.sun.jna.NativeLong;
18 import com.sun.jna.Pointer;
19 import com.sun.jna.platform.unix.LibCAPI.size_t;
20 import com.sun.jna.platform.unix.LibCAPI.ssize_t;
21
22 import oshi.annotation.concurrent.ThreadSafe;
23 import oshi.jna.platform.unix.SolarisLibc;
24 import oshi.jna.platform.unix.SolarisLibc.SolarisLwpsInfo;
25 import oshi.jna.platform.unix.SolarisLibc.SolarisPrUsage;
26 import oshi.jna.platform.unix.SolarisLibc.SolarisPsInfo;
27 import oshi.util.ExecutingCommand;
28 import oshi.util.FileUtil;
29 import oshi.util.ParseUtil;
30 import oshi.util.tuples.Pair;
31 import oshi.util.tuples.Quartet;
32
33
34
35
36 @ThreadSafe
37 public final class PsInfo {
38 private static final Logger LOG = LoggerFactory.getLogger(PsInfo.class);
39
40 private static final SolarisLibc LIBC = SolarisLibc.INSTANCE;
41
42 private static final long PAGE_SIZE = ParseUtil.parseLongOrDefault(ExecutingCommand.getFirstAnswer("pagesize"),
43 4096L);
44
45 private PsInfo() {
46 }
47
48
49
50
51
52
53
54 public static SolarisPsInfo queryPsInfo(int pid) {
55 return new SolarisPsInfo(FileUtil.readAllBytesAsBuffer(String.format(Locale.ROOT, "/proc/%d/psinfo", pid)));
56 }
57
58
59
60
61
62
63
64
65 public static SolarisLwpsInfo queryLwpsInfo(int pid, int tid) {
66 return new SolarisLwpsInfo(
67 FileUtil.readAllBytesAsBuffer(String.format(Locale.ROOT, "/proc/%d/lwp/%d/lwpsinfo", pid, tid)));
68 }
69
70
71
72
73
74
75
76 public static SolarisPrUsage queryPrUsage(int pid) {
77 return new SolarisPrUsage(FileUtil.readAllBytesAsBuffer(String.format(Locale.ROOT, "/proc/%d/usage", pid)));
78 }
79
80
81
82
83
84
85
86
87 public static SolarisPrUsage queryPrUsage(int pid, int tid) {
88 return new SolarisPrUsage(
89 FileUtil.readAllBytesAsBuffer(String.format(Locale.ROOT, "/proc/%d/lwp/%d/usage", pid, tid)));
90 }
91
92
93
94
95
96
97
98
99 public static Quartet<Integer, Long, Long, Byte> queryArgsEnvAddrs(int pid, SolarisPsInfo psinfo) {
100 if (psinfo != null) {
101 int argc = psinfo.pr_argc;
102
103 if (argc > 0) {
104 long argv = Pointer.nativeValue(psinfo.pr_argv);
105 long envp = Pointer.nativeValue(psinfo.pr_envp);
106
107 byte dmodel = psinfo.pr_dmodel;
108
109 if (dmodel * 4 == (envp - argv) / (argc + 1)) {
110 return new Quartet<>(argc, argv, envp, dmodel);
111 }
112 LOG.trace("Failed data model and offset increment sanity check: dm={} diff={}", dmodel, envp - argv);
113 return null;
114 }
115 LOG.trace("Failed argc sanity check: argc={}", argc);
116 return null;
117 }
118 LOG.trace("Failed to read psinfo file for pid: {} ", pid);
119 return null;
120 }
121
122
123
124
125
126
127
128
129 public static Pair<List<String>, Map<String, String>> queryArgsEnv(int pid, SolarisPsInfo psinfo) {
130 List<String> args = new ArrayList<>();
131 Map<String, String> env = new LinkedHashMap<>();
132
133
134 Quartet<Integer, Long, Long, Byte> addrs = queryArgsEnvAddrs(pid, psinfo);
135 if (addrs != null) {
136
137 String procas = "/proc/" + pid + "/as";
138 int fd = LIBC.open(procas, 0);
139 if (fd < 0) {
140 LOG.trace("No permission to read file: {} ", procas);
141 return new Pair<>(args, env);
142 }
143 try {
144
145 int argc = addrs.getA();
146 long argv = addrs.getB();
147 long envp = addrs.getC();
148 long increment = addrs.getD() * 4L;
149
150
151 long bufStart = 0;
152 try (Memory buffer = new Memory(PAGE_SIZE * 2)) {
153 size_t bufSize = new size_t(buffer.size());
154
155
156
157 long[] argp = new long[argc];
158 long offset = argv;
159 for (int i = 0; i < argc; i++) {
160 bufStart = conditionallyReadBufferFromStartOfPage(fd, buffer, bufSize, bufStart, offset);
161 argp[i] = bufStart == 0 ? 0 : getOffsetFromBuffer(buffer, offset - bufStart, increment);
162 offset += increment;
163 }
164
165
166
167 List<Long> envPtrList = new ArrayList<>();
168 offset = envp;
169 long addr = 0;
170 int limit = 500;
171 do {
172 bufStart = conditionallyReadBufferFromStartOfPage(fd, buffer, bufSize, bufStart, offset);
173 addr = bufStart == 0 ? 0 : getOffsetFromBuffer(buffer, offset - bufStart, increment);
174 if (addr != 0) {
175 envPtrList.add(addr);
176 }
177 offset += increment;
178 } while (addr != 0 && --limit > 0);
179
180
181 for (int i = 0; i < argp.length && argp[i] != 0; i++) {
182 bufStart = conditionallyReadBufferFromStartOfPage(fd, buffer, bufSize, bufStart, argp[i]);
183 if (bufStart != 0) {
184 String argStr = buffer.getString(argp[i] - bufStart);
185 if (!argStr.isEmpty()) {
186 args.add(argStr);
187 }
188 }
189 }
190
191
192 for (Long envPtr : envPtrList) {
193 bufStart = conditionallyReadBufferFromStartOfPage(fd, buffer, bufSize, bufStart, envPtr);
194 if (bufStart != 0) {
195 String envStr = buffer.getString(envPtr - bufStart);
196 int idx = envStr.indexOf('=');
197 if (idx > 0) {
198 env.put(envStr.substring(0, idx), envStr.substring(idx + 1));
199 }
200 }
201 }
202 }
203 } finally {
204 LIBC.close(fd);
205 }
206 }
207 return new Pair<>(args, env);
208 }
209
210
211
212
213
214
215
216
217
218
219
220
221 private static long conditionallyReadBufferFromStartOfPage(int fd, Memory buffer, size_t bufSize, long bufStart,
222 long addr) {
223
224 if (addr < bufStart || addr - bufStart > PAGE_SIZE) {
225 long newStart = Math.floorDiv(addr, PAGE_SIZE) * PAGE_SIZE;
226 ssize_t result = LIBC.pread(fd, buffer, bufSize, new NativeLong(newStart));
227
228 if (result.longValue() < PAGE_SIZE) {
229 LOG.debug("Failed to read page from address space: {} bytes read", result.longValue());
230 return 0;
231 }
232 return newStart;
233 }
234 return bufStart;
235 }
236
237 private static long getOffsetFromBuffer(Memory buffer, long offset, long increment) {
238 return increment == 8 ? buffer.getLong(offset) : buffer.getInt(offset);
239 }
240 }