├── LICENSE ├── README.md ├── build.xml ├── javasysmon.iml ├── javasysmon.ipr ├── lib ├── java │ └── junit-3.8.2.jar └── native │ ├── javasysmon.dll │ ├── javasysmon64.dll │ ├── javasysmonsolx86.so │ └── libjavasysmon.jnilib └── src ├── main ├── c │ ├── macosx │ │ ├── javasysmon.xcodeproj │ │ │ └── project.pbxproj │ │ ├── sysinfo.c │ │ └── sysinfo.h │ ├── solaris │ │ ├── build │ │ ├── javasysmon.c │ │ └── javasysmon.h │ └── windows │ │ ├── Psapi.Lib │ │ ├── javasysmon.c │ │ ├── javasysmon.h │ │ ├── javasysmon.vcproj │ │ └── x64 │ │ └── Psapi.Lib └── java │ └── com │ └── jezhumble │ └── javasysmon │ ├── CpuTimes.java │ ├── FileUtils.java │ ├── JavaSysMon.java │ ├── LinuxMonitor.java │ ├── LinuxProcessInfoParser.java │ ├── MacOsXMonitor.java │ ├── MemoryStats.java │ ├── Monitor.java │ ├── NativeLibraryLoader.java │ ├── NullMonitor.java │ ├── OsProcess.java │ ├── ParseException.java │ ├── ProcessInfo.java │ ├── ProcessVisitor.java │ ├── SolarisMonitor.java │ ├── UnixPasswdParser.java │ ├── WindowsMonitor.java │ └── package.html └── test ├── java └── com │ └── jezhumble │ └── javasysmon │ ├── CpuTimesTest.java │ ├── LinuxMonitorTest.java │ ├── LinuxProcessInfoParserTest.java │ ├── NativeLibraryLoaderTest.java │ ├── OsProcessTest.java │ ├── ProcessTreeFunctionalTest.java │ ├── StubFileUtils.java │ └── UnixPasswdParserTest.java └── resources ├── test_cpuinfo ├── test_meminfo ├── test_self_stat ├── test_stat_0 ├── test_stat_1 ├── test_stat_expected ├── test_stat_few_fields ├── test_stat_invalid_numbers ├── test_stat_no_parens ├── test_stat_numbers ├── test_stat_paren ├── test_stat_spaces ├── test_status └── test_uptime /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2009 ThoughtWorks, Inc. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are 4 | permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this list of 7 | conditions and the following disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright notice, this list 10 | of conditions and the following disclaimer in the documentation and/or other materials 11 | provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THOUGHTWORKS, INC. ``AS IS'' AND ANY EXPRESS OR IMPLIED 14 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 15 | FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THOUGHTWORKS, INC. OR 16 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 17 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 18 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 19 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 20 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 21 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | 23 | The views and conclusions contained in the software and documentation are those of the 24 | authors and should not be interpreted as representing official policies, either expressed 25 | or implied, of ThoughtWorks, Inc. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | JavaSysMon 2 | ========== 3 | 4 | JavaSysMon is designed to provide an OS-independent way to manage OS processes and get live system performance information such as CPU and memory usage, distributed as a single jar file. It is written in C and Java. However the native binaries are hidden away inside the jar, so you never need to worry about them. 5 | 6 | Currently it supports Mac OS X, Linux, Windows, and Solaris. Ultimately we aim to support everything from AIX to Android. 7 | 8 | If you’re interested in adding support for a new platform, check out the [project wiki](http://wiki.github.com/jezhumble/javasysmon). 9 | 10 | Download latest 11 | --------------- 12 | 13 | The current version of JavaSysMon is 0.3.5, released May 14th 2014. You can get it here: http://continuousdelivery.com/downloads/javasysmon/javasysmon-0.3.5.jar 14 | 15 | Run it with java -jar 16 | 17 | Building and running 18 | -------------------- 19 | 20 | Run ant, and then: 21 | 22 | java -jar target/javasysmon.jar 23 | 24 | Using the library from code 25 | --------------------------- 26 | 27 | Simply put the jar in your classpath, and use it like this: 28 | 29 | import com.jezhumble.javasysmon.JavaSysMon; 30 | 31 | JavaSysMon monitor = new JavaSysMon(); 32 | String osName = monitor.osName(); 33 | etc... 34 | 35 | For full details of the API, consult the [JavaDoc](http://jezhumble.github.com/javasysmon/) 36 | 37 | Current support and limitations 38 | ------------------------------- 39 | 40 | * Currently supports Mac OS X, Linux, Windows, and Solaris 41 | * Solaris binary is compiled on x86 on OpenSolaris, so it won't work on SPARC, and has not been tested on SunOS < 5.11 42 | * Solaris CPU usage only correctly reports usage for first CPU. 43 | * Supports Java 1.4 and above 44 | * CPU speed on Linux only reports correct values for Intel CPUs 45 | 46 | Source code 47 | ----------- 48 | 49 | The Java source code sits under src/main/java. The C source code is in src/main/c, with a subdirectory for each platform supported by JavaSysMon. The compiled binaries are stored in lib/native, and it is these that are used to build the jar when you run ant. So if you change the c source, you'll need to compile and copy the binary to lib/native before running ant in order to test your changes. 50 | 51 | License 52 | ------- 53 | 54 | JavaSysMon uses the NetBSD (2-line) license. 55 | 56 | Links 57 | ----- 58 | 59 | * [Source code](http://github.com/jezhumble/javasysmon) 60 | * [Wiki](http://wiki.github.com/jezhumble/javasysmon) 61 | * [JavaDoc](http://jezhumble.github.com/javasysmon/) 62 | * [Bugs/Features](http://github.com/arya/javasysmon/issues) 63 | * [Mailing List](http://groups.google.com/group/javasysmon) 64 | -------------------------------------------------------------------------------- /build.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 68 | Java System Monitor]]> 69 | Copyright © 2009 ThoughtWorks. All Rights Reserved.]]> 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /javasysmon.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /javasysmon.ipr: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | $PROJECT_DIR$/target/test-classes 9 | 10 | 11 | 12 | 13 | 14 | 15 | 34 | 35 | 36 | 37 | 38 | 40 | 41 | 42 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 191 | 192 | 193 | 194 | 195 | 196 | -------------------------------------------------------------------------------- /lib/java/junit-3.8.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jezhumble/javasysmon/4caef77a5b071abb6662eaa07acf376821277112/lib/java/junit-3.8.2.jar -------------------------------------------------------------------------------- /lib/native/javasysmon.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jezhumble/javasysmon/4caef77a5b071abb6662eaa07acf376821277112/lib/native/javasysmon.dll -------------------------------------------------------------------------------- /lib/native/javasysmon64.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jezhumble/javasysmon/4caef77a5b071abb6662eaa07acf376821277112/lib/native/javasysmon64.dll -------------------------------------------------------------------------------- /lib/native/javasysmonsolx86.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jezhumble/javasysmon/4caef77a5b071abb6662eaa07acf376821277112/lib/native/javasysmonsolx86.so -------------------------------------------------------------------------------- /lib/native/libjavasysmon.jnilib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jezhumble/javasysmon/4caef77a5b071abb6662eaa07acf376821277112/lib/native/libjavasysmon.jnilib -------------------------------------------------------------------------------- /src/main/c/macosx/javasysmon.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 45; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | ED15C7EC10D1F155008C3F45 /* sysinfo.h in Headers */ = {isa = PBXBuildFile; fileRef = ED15C7EA10D1F155008C3F45 /* sysinfo.h */; }; 11 | ED15C7ED10D1F155008C3F45 /* sysinfo.c in Sources */ = {isa = PBXBuildFile; fileRef = ED15C7EB10D1F155008C3F45 /* sysinfo.c */; }; 12 | ED5DCBED10D48FD800ED1DC0 /* JavaVM.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ED5DCBEC10D48FD800ED1DC0 /* JavaVM.framework */; }; 13 | /* End PBXBuildFile section */ 14 | 15 | /* Begin PBXFileReference section */ 16 | ED15C7EA10D1F155008C3F45 /* sysinfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sysinfo.h; sourceTree = ""; }; 17 | ED15C7EB10D1F155008C3F45 /* sysinfo.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sysinfo.c; sourceTree = ""; }; 18 | ED5DCBEC10D48FD800ED1DC0 /* JavaVM.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaVM.framework; path = System/Library/Frameworks/JavaVM.framework; sourceTree = SDKROOT; }; 19 | ED5DCCC810D5971D00ED1DC0 /* libjavasysmon.jnilib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libjavasysmon.jnilib; sourceTree = BUILT_PRODUCTS_DIR; }; 20 | /* End PBXFileReference section */ 21 | 22 | /* Begin PBXFrameworksBuildPhase section */ 23 | D289988505E68E00004EDB86 /* Frameworks */ = { 24 | isa = PBXFrameworksBuildPhase; 25 | buildActionMask = 2147483647; 26 | files = ( 27 | ED5DCBED10D48FD800ED1DC0 /* JavaVM.framework in Frameworks */, 28 | ); 29 | runOnlyForDeploymentPostprocessing = 0; 30 | }; 31 | /* End PBXFrameworksBuildPhase section */ 32 | 33 | /* Begin PBXGroup section */ 34 | 08FB7794FE84155DC02AAC07 /* javanativetools */ = { 35 | isa = PBXGroup; 36 | children = ( 37 | 08FB7795FE84155DC02AAC07 /* Source */, 38 | 1AB674ADFE9D54B511CA2CBB /* Products */, 39 | ED5DCBEC10D48FD800ED1DC0 /* JavaVM.framework */, 40 | ); 41 | name = javanativetools; 42 | sourceTree = ""; 43 | }; 44 | 08FB7795FE84155DC02AAC07 /* Source */ = { 45 | isa = PBXGroup; 46 | children = ( 47 | ED15C7EA10D1F155008C3F45 /* sysinfo.h */, 48 | ED15C7EB10D1F155008C3F45 /* sysinfo.c */, 49 | ); 50 | name = Source; 51 | sourceTree = ""; 52 | }; 53 | 1AB674ADFE9D54B511CA2CBB /* Products */ = { 54 | isa = PBXGroup; 55 | children = ( 56 | ED5DCCC810D5971D00ED1DC0 /* libjavasysmon.jnilib */, 57 | ); 58 | name = Products; 59 | sourceTree = ""; 60 | }; 61 | /* End PBXGroup section */ 62 | 63 | /* Begin PBXHeadersBuildPhase section */ 64 | D2AAC0600554660B00DB518D /* Headers */ = { 65 | isa = PBXHeadersBuildPhase; 66 | buildActionMask = 2147483647; 67 | files = ( 68 | ED15C7EC10D1F155008C3F45 /* sysinfo.h in Headers */, 69 | ); 70 | runOnlyForDeploymentPostprocessing = 0; 71 | }; 72 | /* End PBXHeadersBuildPhase section */ 73 | 74 | /* Begin PBXNativeTarget section */ 75 | D2AAC0620554660B00DB518D /* javasysmon */ = { 76 | isa = PBXNativeTarget; 77 | buildConfigurationList = 1DEB914A08733D8E0010E9CD /* Build configuration list for PBXNativeTarget "javasysmon" */; 78 | buildPhases = ( 79 | D2AAC0600554660B00DB518D /* Headers */, 80 | D2AAC0610554660B00DB518D /* Sources */, 81 | D289988505E68E00004EDB86 /* Frameworks */, 82 | ); 83 | buildRules = ( 84 | ); 85 | dependencies = ( 86 | ); 87 | name = javasysmon; 88 | productName = javanativetools; 89 | productReference = ED5DCCC810D5971D00ED1DC0 /* libjavasysmon.jnilib */; 90 | productType = "com.apple.product-type.library.dynamic"; 91 | }; 92 | /* End PBXNativeTarget section */ 93 | 94 | /* Begin PBXProject section */ 95 | 08FB7793FE84155DC02AAC07 /* Project object */ = { 96 | isa = PBXProject; 97 | buildConfigurationList = 1DEB914E08733D8E0010E9CD /* Build configuration list for PBXProject "javasysmon" */; 98 | compatibilityVersion = "Xcode 3.1"; 99 | hasScannedForEncodings = 1; 100 | mainGroup = 08FB7794FE84155DC02AAC07 /* javanativetools */; 101 | projectDirPath = ""; 102 | projectRoot = ""; 103 | targets = ( 104 | D2AAC0620554660B00DB518D /* javasysmon */, 105 | ); 106 | }; 107 | /* End PBXProject section */ 108 | 109 | /* Begin PBXSourcesBuildPhase section */ 110 | D2AAC0610554660B00DB518D /* Sources */ = { 111 | isa = PBXSourcesBuildPhase; 112 | buildActionMask = 2147483647; 113 | files = ( 114 | ED15C7ED10D1F155008C3F45 /* sysinfo.c in Sources */, 115 | ); 116 | runOnlyForDeploymentPostprocessing = 0; 117 | }; 118 | /* End PBXSourcesBuildPhase section */ 119 | 120 | /* Begin XCBuildConfiguration section */ 121 | 1DEB914B08733D8E0010E9CD /* Debug */ = { 122 | isa = XCBuildConfiguration; 123 | buildSettings = { 124 | ALWAYS_SEARCH_USER_PATHS = NO; 125 | ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; 126 | COPY_PHASE_STRIP = NO; 127 | EXECUTABLE_EXTENSION = jnilib; 128 | EXECUTABLE_PREFIX = lib; 129 | GCC_DYNAMIC_NO_PIC = NO; 130 | GCC_ENABLE_FIX_AND_CONTINUE = YES; 131 | GCC_MODEL_TUNING = G5; 132 | GCC_OPTIMIZATION_LEVEL = 0; 133 | HEADER_SEARCH_PATHS = "$(SDKROOT)/System/Library/Frameworks/JavaVM.framework/Headers"; 134 | INSTALL_PATH = /usr/local/lib; 135 | ONLY_ACTIVE_ARCH = NO; 136 | PRODUCT_NAME = javasysmon; 137 | }; 138 | name = Debug; 139 | }; 140 | 1DEB914C08733D8E0010E9CD /* Release */ = { 141 | isa = XCBuildConfiguration; 142 | buildSettings = { 143 | ALWAYS_SEARCH_USER_PATHS = NO; 144 | ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; 145 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 146 | EXECUTABLE_EXTENSION = jnilib; 147 | EXECUTABLE_PREFIX = lib; 148 | GCC_MODEL_TUNING = G5; 149 | HEADER_SEARCH_PATHS = "$(SDKROOT)/System/Library/Frameworks/JavaVM.framework/Headers"; 150 | INSTALL_PATH = /usr/local/lib; 151 | ONLY_ACTIVE_ARCH = NO; 152 | PRODUCT_NAME = javasysmon; 153 | }; 154 | name = Release; 155 | }; 156 | 1DEB914F08733D8E0010E9CD /* Debug */ = { 157 | isa = XCBuildConfiguration; 158 | buildSettings = { 159 | ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; 160 | GCC_C_LANGUAGE_STANDARD = gnu99; 161 | GCC_OPTIMIZATION_LEVEL = 0; 162 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 163 | GCC_WARN_UNUSED_VARIABLE = YES; 164 | MACOSX_DEPLOYMENT_TARGET = 10.4; 165 | ONLY_ACTIVE_ARCH = NO; 166 | PREBINDING = NO; 167 | SDKROOT = macosx10.6; 168 | }; 169 | name = Debug; 170 | }; 171 | 1DEB915008733D8E0010E9CD /* Release */ = { 172 | isa = XCBuildConfiguration; 173 | buildSettings = { 174 | ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; 175 | GCC_C_LANGUAGE_STANDARD = gnu99; 176 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 177 | GCC_WARN_UNUSED_VARIABLE = YES; 178 | MACOSX_DEPLOYMENT_TARGET = 10.4; 179 | ONLY_ACTIVE_ARCH = NO; 180 | PREBINDING = NO; 181 | SDKROOT = macosx10.6; 182 | }; 183 | name = Release; 184 | }; 185 | /* End XCBuildConfiguration section */ 186 | 187 | /* Begin XCConfigurationList section */ 188 | 1DEB914A08733D8E0010E9CD /* Build configuration list for PBXNativeTarget "javasysmon" */ = { 189 | isa = XCConfigurationList; 190 | buildConfigurations = ( 191 | 1DEB914B08733D8E0010E9CD /* Debug */, 192 | 1DEB914C08733D8E0010E9CD /* Release */, 193 | ); 194 | defaultConfigurationIsVisible = 0; 195 | defaultConfigurationName = Release; 196 | }; 197 | 1DEB914E08733D8E0010E9CD /* Build configuration list for PBXProject "javasysmon" */ = { 198 | isa = XCConfigurationList; 199 | buildConfigurations = ( 200 | 1DEB914F08733D8E0010E9CD /* Debug */, 201 | 1DEB915008733D8E0010E9CD /* Release */, 202 | ); 203 | defaultConfigurationIsVisible = 0; 204 | defaultConfigurationName = Release; 205 | }; 206 | /* End XCConfigurationList section */ 207 | }; 208 | rootObject = 08FB7793FE84155DC02AAC07 /* Project object */; 209 | } 210 | -------------------------------------------------------------------------------- /src/main/c/macosx/sysinfo.c: -------------------------------------------------------------------------------- 1 | /* 2 | * sysinfo.c 3 | * javanativetools 4 | * 5 | * Created by Jez Humble on 10 Dec 2009. 6 | * Copyright 2009 Jez Humble. All rights reserved. 7 | * Licensed under the terms of the New BSD license. 8 | */ 9 | 10 | #include "sysinfo.h" 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | static int pageSize = 0; 27 | static mach_port_t sysmonport; 28 | static unsigned long long total_memory, ticks_per_second; 29 | typedef struct kinfo_proc kinfo_proc; 30 | 31 | int sample_cpu_ticks(host_info_t host_info) 32 | { 33 | kern_return_t error; 34 | mach_msg_type_number_t count; 35 | 36 | count = HOST_CPU_LOAD_INFO_COUNT; 37 | error = host_statistics(sysmonport, HOST_CPU_LOAD_INFO, host_info, &count); 38 | if (error != KERN_SUCCESS) { 39 | printf("Error trying to get CPU usage: %s\n", mach_error_string(error)); 40 | return 1; 41 | } 42 | return 0; 43 | } 44 | 45 | JNIEXPORT jint JNICALL JNI_OnLoad (JavaVM * vm, void * reserved) 46 | { 47 | // Get some system information that won't change that we'll need later on 48 | int mib[2]; 49 | size_t len; 50 | sysmonport = mach_host_self(); 51 | ticks_per_second = sysconf(_SC_CLK_TCK); 52 | 53 | mib[0] = CTL_HW; 54 | mib[1] = HW_PAGESIZE; 55 | len = sizeof(pageSize); 56 | 57 | if (sysctl(mib, 2, &pageSize, &len, NULL, 0) == -1) { 58 | perror("sysctl"); 59 | } 60 | 61 | mib[0] = CTL_HW; 62 | mib[1] = HW_MEMSIZE; 63 | len = sizeof(total_memory); 64 | 65 | if (sysctl(mib, 2, &total_memory, &len, NULL, 0) != 0) { 66 | perror("sysctl"); 67 | } 68 | 69 | return JNI_VERSION_1_2; 70 | } 71 | 72 | JNIEXPORT jobject JNICALL Java_com_jezhumble_javasysmon_MacOsXMonitor_cpuTimes (JNIEnv *env, jobject obj) 73 | { 74 | host_cpu_load_info_data_t cpu_stats; 75 | unsigned long long userticks, systicks, idleticks; 76 | jclass cpu_times_class; 77 | jmethodID cpu_times_constructor; 78 | jobject cpu_times; 79 | 80 | if (sample_cpu_ticks((host_info_t) &cpu_stats) == 0) { 81 | userticks = cpu_stats.cpu_ticks[CPU_STATE_USER] + cpu_stats.cpu_ticks[CPU_STATE_NICE]; 82 | systicks = cpu_stats.cpu_ticks[CPU_STATE_SYSTEM]; 83 | idleticks = cpu_stats.cpu_ticks[CPU_STATE_IDLE]; 84 | 85 | cpu_times_class = (*env)->FindClass(env, "com/jezhumble/javasysmon/CpuTimes"); 86 | cpu_times_constructor = (*env)->GetMethodID(env, cpu_times_class, "", "(JJJ)V"); 87 | cpu_times = (*env)->NewObject(env, cpu_times_class, cpu_times_constructor, 88 | (jlong) (userticks * 1000) / ticks_per_second, 89 | (jlong) (systicks * 1000) / ticks_per_second, 90 | (jlong) (idleticks * 1000) / ticks_per_second); 91 | (*env)->DeleteLocalRef(env, cpu_times_class); 92 | return cpu_times; 93 | } else { 94 | return NULL; 95 | } 96 | } 97 | 98 | JNIEXPORT jobject JNICALL Java_com_jezhumble_javasysmon_MacOsXMonitor_physical (JNIEnv *env, jobject obj) 99 | { 100 | kern_return_t error; 101 | mach_msg_type_number_t count; 102 | struct vm_statistics r_vm_info; 103 | unsigned long long mem_free; 104 | jclass memory_stats_class; 105 | jmethodID memory_stats_constructor; 106 | jobject memory_stats; 107 | 108 | count = sizeof(r_vm_info) / sizeof(natural_t); 109 | error = host_statistics(sysmonport, HOST_VM_INFO, (host_info_t) &r_vm_info, &count); 110 | if (error != KERN_SUCCESS) { 111 | printf("Error trying to get free memory: %s\n", mach_error_string(error)); 112 | return NULL; 113 | } 114 | mem_free = (unsigned long long) r_vm_info.free_count * pageSize; 115 | 116 | memory_stats_class = (*env)->FindClass(env, "com/jezhumble/javasysmon/MemoryStats"); 117 | memory_stats_constructor = (*env)->GetMethodID(env, memory_stats_class, "", "(JJ)V"); 118 | memory_stats = (*env)->NewObject(env, memory_stats_class, memory_stats_constructor, (jlong) mem_free, (jlong) total_memory); 119 | (*env)->DeleteLocalRef(env, memory_stats_class); 120 | return memory_stats; 121 | } 122 | 123 | JNIEXPORT jobject JNICALL Java_com_jezhumble_javasysmon_MacOsXMonitor_swap (JNIEnv *env, jobject obj) 124 | { 125 | int mib[2]; 126 | size_t len; 127 | struct xsw_usage xsu; 128 | jclass memory_stats_class; 129 | jmethodID memory_stats_constructor; 130 | jobject memory_stats; 131 | 132 | mib[0] = CTL_VM; 133 | mib[1] = VM_SWAPUSAGE; 134 | len = sizeof(xsu); 135 | 136 | if (sysctl(mib, 2, &xsu, &len, NULL, 0) != 0) { 137 | perror("sysctl"); 138 | return (jlong) 0; 139 | } 140 | 141 | memory_stats_class = (*env)->FindClass(env, "com/jezhumble/javasysmon/MemoryStats"); 142 | memory_stats_constructor = (*env)->GetMethodID(env, memory_stats_class, "", "(JJ)V"); 143 | memory_stats = (*env)->NewObject(env, memory_stats_class, memory_stats_constructor, (jlong) xsu.xsu_avail, (jlong) xsu.xsu_total); 144 | (*env)->DeleteLocalRef(env, memory_stats_class); 145 | return memory_stats; 146 | } 147 | 148 | JNIEXPORT jint JNICALL Java_com_jezhumble_javasysmon_MacOsXMonitor_numCpus (JNIEnv *env, jobject object) 149 | { 150 | return (jint) sysconf(_SC_NPROCESSORS_CONF); 151 | } 152 | 153 | JNIEXPORT jlong JNICALL Java_com_jezhumble_javasysmon_MacOsXMonitor_cpuFrequencyInHz (JNIEnv *env, jobject object) 154 | { 155 | int mib[2]; 156 | size_t len; 157 | unsigned int cpu_freq; 158 | 159 | mib[0] = CTL_HW; 160 | mib[1] = HW_CPU_FREQ; 161 | len = sizeof(cpu_freq); 162 | 163 | if (sysctl(mib, 2, &cpu_freq, &len, NULL, 0) != 0) { 164 | perror("sysctl"); 165 | return (jlong) 0; 166 | } 167 | 168 | return (jlong) cpu_freq; 169 | } 170 | 171 | JNIEXPORT jlong JNICALL Java_com_jezhumble_javasysmon_MacOsXMonitor_uptimeInSeconds (JNIEnv *env, jobject object) 172 | { 173 | int mib[2]; 174 | size_t len; 175 | struct timeval secs; 176 | unsigned long long uptime; 177 | 178 | if (gettimeofday(&secs, NULL) != 0) { 179 | perror("gettimeofday"); 180 | return (jlong) 0; 181 | } 182 | 183 | uptime = (unsigned long long) secs.tv_sec; 184 | 185 | mib[0] = CTL_KERN; 186 | mib[1] = KERN_BOOTTIME; 187 | len = sizeof(secs); 188 | 189 | if (sysctl(mib, 2, &secs, &len, NULL, 0) != 0) { 190 | perror("sysctl"); 191 | return (jlong) 0; 192 | } 193 | 194 | uptime -= (unsigned long long) secs.tv_sec; 195 | 196 | return (jlong) uptime; 197 | } 198 | 199 | JNIEXPORT jint JNICALL Java_com_jezhumble_javasysmon_MacOsXMonitor_currentPid (JNIEnv *env, jobject object) 200 | { 201 | return (jint) getpid(); 202 | } 203 | 204 | unsigned long long get_millisecs(struct timeval timeval) { 205 | return timeval.tv_sec * 1000 + timeval.tv_usec / 1000; 206 | } 207 | 208 | JNIEXPORT jobjectArray JNICALL Java_com_jezhumble_javasysmon_MacOsXMonitor_processTable (JNIEnv *env, jobject object) 209 | { 210 | jclass process_info_class; 211 | jmethodID process_info_constructor; 212 | jobject process_info; 213 | jobjectArray process_info_array; 214 | kinfo_proc * result; 215 | struct task_basic_info tasks_info; 216 | struct rusage rusage; 217 | struct passwd * passwd; 218 | bool done, is_me; 219 | unsigned int info_count = TASK_BASIC_INFO_COUNT; 220 | static const int name[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0 }; 221 | size_t length; 222 | int count, i, err; 223 | 224 | // Thanks to http://developer.apple.com/mac/library/qa/qa2001/qa1123.html 225 | // This sysctl is the only way to do this on OSX without having elevated privileges 226 | 227 | result = NULL; 228 | done = false; 229 | do { 230 | if (result != NULL) { 231 | return NULL; 232 | } 233 | 234 | // Call sysctl with a NULL buffer. 235 | 236 | length = 0; 237 | err = sysctl( (int *) name, (sizeof(name) / sizeof(*name)) - 1, 238 | NULL, &length, 239 | NULL, 0); 240 | if (err == -1) { 241 | err = errno; 242 | } 243 | 244 | // Allocate an appropriately sized buffer based on the results 245 | // from the previous call. 246 | 247 | if (err == 0) { 248 | result = malloc(length); 249 | if (result == NULL) { 250 | err = ENOMEM; 251 | } 252 | } 253 | 254 | // Call sysctl again with the new buffer. If we get an ENOMEM 255 | // error, toss away our buffer and start again. 256 | 257 | if (err == 0) { 258 | err = sysctl( (int *) name, (sizeof(name) / sizeof(*name)) - 1, 259 | result, &length, 260 | NULL, 0); 261 | if (err == -1) { 262 | err = errno; 263 | } 264 | if (err == 0) { 265 | done = true; 266 | } else if (err == ENOMEM) { 267 | free(result); 268 | result = NULL; 269 | err = 0; 270 | } 271 | } 272 | } while (err == 0 && ! done); 273 | 274 | // This only works for me (since Tiger) unless you have elevated privileges. Lame. 275 | task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t) &tasks_info, &info_count); 276 | getrusage(RUSAGE_SELF, &rusage); 277 | 278 | // if anybody wants to get the actual command that started the process, check out: 279 | // http://cpansearch.perl.org/src/DURIST/Proc-ProcessTable-0.42/os/darwin.c 280 | 281 | if (err == 0) { 282 | count = length / sizeof(kinfo_proc); 283 | process_info_array = (*env)->NewObjectArray(env, count, (*env)->FindClass(env, "com/jezhumble/javasysmon/ProcessInfo"), NULL); 284 | process_info_class = (*env)->FindClass(env, "com/jezhumble/javasysmon/ProcessInfo"); 285 | process_info_constructor = (*env)->GetMethodID(env, process_info_class, "", 286 | "(IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;JJJJ)V"); 287 | for (i = 0; i < count; i++) { 288 | is_me = getpid() == result[i].kp_proc.p_pid; 289 | passwd = getpwuid(result[i].kp_eproc.e_pcred.p_ruid); 290 | process_info = (*env)->NewObject(env, process_info_class, process_info_constructor, 291 | (jint) result[i].kp_proc.p_pid, 292 | (jint) result[i].kp_eproc.e_ppid, 293 | (*env)->NewStringUTF(env, ""), 294 | (*env)->NewStringUTF(env, result[i].kp_proc.p_comm), 295 | (*env)->NewStringUTF(env, passwd->pw_name), 296 | (jlong) is_me ? get_millisecs(rusage.ru_utime) : 0, 297 | (jlong) is_me ? get_millisecs(rusage.ru_stime) : 0, 298 | (jlong) is_me ? tasks_info.resident_size : 0, 299 | (jlong) is_me ? tasks_info.virtual_size : 0); 300 | (*env)->SetObjectArrayElement(env, process_info_array, i, process_info); 301 | } 302 | (*env)->DeleteLocalRef(env, process_info_class); 303 | } 304 | 305 | if (result != NULL) { 306 | free(result); 307 | return process_info_array; 308 | } 309 | 310 | return NULL; 311 | } 312 | 313 | JNIEXPORT void JNICALL Java_com_jezhumble_javasysmon_MacOsXMonitor_killProcess (JNIEnv *env, jobject object, jint pid) { 314 | kill(pid, SIGTERM); 315 | } 316 | -------------------------------------------------------------------------------- /src/main/c/macosx/sysinfo.h: -------------------------------------------------------------------------------- 1 | /* DO NOT EDIT THIS FILE - it is machine generated */ 2 | #include 3 | /* Header for class com_jezhumble_javasysmon_MacOsXMonitor */ 4 | 5 | #ifndef _Included_com_jezhumble_javasysmon_MacOsXMonitor 6 | #define _Included_com_jezhumble_javasysmon_MacOsXMonitor 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | /* 12 | * Class: com_jezhumble_javasysmon_MacOsXMonitor 13 | * Method: numCpus 14 | * Signature: ()I 15 | */ 16 | JNIEXPORT jint JNICALL Java_com_jezhumble_javasysmon_MacOsXMonitor_numCpus 17 | (JNIEnv *, jobject); 18 | 19 | /* 20 | * Class: com_jezhumble_javasysmon_MacOsXMonitor 21 | * Method: cpuFrequencyInHz 22 | * Signature: ()I 23 | */ 24 | JNIEXPORT jlong JNICALL Java_com_jezhumble_javasysmon_MacOsXMonitor_cpuFrequencyInHz 25 | (JNIEnv *, jobject); 26 | 27 | /* 28 | * Class: com_jezhumble_javasysmon_MacOsXMonitor 29 | * Method: uptimeInSeconds 30 | * Signature: ()J 31 | */ 32 | JNIEXPORT jlong JNICALL Java_com_jezhumble_javasysmon_MacOsXMonitor_uptimeInSeconds 33 | (JNIEnv *, jobject); 34 | 35 | /* 36 | * Class: com_jezhumble_javasysmon_MacOsXMonitor 37 | * Method: numCpus 38 | * Signature: ()I 39 | */ 40 | JNIEXPORT jint JNICALL Java_com_jezhumble_javasysmon_MacOsXMonitor_currentPid 41 | (JNIEnv *, jobject); 42 | 43 | /* 44 | * Class: com_jezhumble_javasysmon_MacOsXMonitor 45 | * Method: cpuTimes 46 | * Signature: ()Lcom/jezhumble/javasysmon/CpuTimes; 47 | */ 48 | JNIEXPORT jobject JNICALL Java_com_jezhumble_javasysmon_MacOsXMonitor_cpuTimes 49 | (JNIEnv *, jobject); 50 | 51 | /* 52 | * Class: com_jezhumble_javasysmon_MacOsXMonitor 53 | * Method: physical 54 | * Signature: ()Lcom/jezhumble/javasysmon/MemoryStats; 55 | */ 56 | JNIEXPORT jobject JNICALL Java_com_jezhumble_javasysmon_MacOsXMonitor_physical 57 | (JNIEnv *, jobject); 58 | 59 | /* 60 | * Class: com_jezhumble_javasysmon_MacOsXMonitor 61 | * Method: swap 62 | * Signature: ()Lcom/jezhumble/javasysmon/MemoryStats; 63 | */ 64 | JNIEXPORT jobject JNICALL Java_com_jezhumble_javasysmon_MacOsXMonitor_swap 65 | (JNIEnv *, jobject); 66 | 67 | /* 68 | * Class: com_jezhumble_javasysmon_MacOsXMonitor 69 | * Method: processTable 70 | * Signature: ()[Lcom/jezhumble/javasysmon/ProcessInfo; 71 | */ 72 | JNIEXPORT jobjectArray JNICALL Java_com_jezhumble_javasysmon_MacOsXMonitor_processTable 73 | (JNIEnv *, jobject); 74 | 75 | /* 76 | * Class: com_jezhumble_javasysmon_MacOsXMonitor 77 | * Method: killProcess 78 | * Signature: (I)V 79 | */ 80 | JNIEXPORT void JNICALL Java_com_jezhumble_javasysmon_MacOsXMonitor_killProcess 81 | (JNIEnv *, jobject, jint); 82 | 83 | 84 | #ifdef __cplusplus 85 | } 86 | #endif 87 | #endif 88 | -------------------------------------------------------------------------------- /src/main/c/solaris/build: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | cc -G -I/usr/java/include -I/usr/java/include/solaris -h javasysmonsolx86.so -lkstat -o javasysmonsolx86.so javasysmon.c 3 | cp *.so ../lib/native/ 4 | -------------------------------------------------------------------------------- /src/main/c/solaris/javasysmon.c: -------------------------------------------------------------------------------- 1 | /* 2 | * javasysmon.c 3 | * javanativetools 4 | * 5 | * Created by Jez Humble on 15 Dec 2009. 6 | * Copyright 2009 Jez Humble. All rights reserved. 7 | * Licensed under the terms of the New BSD license. 8 | * Thanks to: http://getthegood.com/TechNotes/Papers/ProcStatistics.html 9 | */ 10 | #include "javasysmon.h" 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #define MAXSTRSIZE 80 26 | 27 | static int num_cpus; 28 | static int pagesize; 29 | static unsigned long long phys_mem; 30 | 31 | long timespec_to_millisecs(timestruc_t time) { 32 | return (time.tv_sec * 1000) + (time.tv_nsec / 1000000); 33 | } 34 | 35 | JNIEXPORT jint JNICALL JNI_OnLoad (JavaVM * vm, void * reserved) 36 | { 37 | num_cpus = sysconf(_SC_NPROCESSORS_ONLN); 38 | pagesize = sysconf(_SC_PAGESIZE); 39 | phys_mem = sysconf(_SC_PHYS_PAGES) * pagesize; 40 | 41 | return JNI_VERSION_1_2; 42 | } 43 | 44 | JNIEXPORT jobject JNICALL Java_com_jezhumble_javasysmon_SolarisMonitor_cpuTimes (JNIEnv *env, jobject obj) 45 | { 46 | kstat_ctl_t *kc; 47 | kstat_t *ksp; 48 | struct cpu_stat cpu_stats; 49 | int i; 50 | unsigned long long userticks, systicks, idleticks; 51 | jclass cpu_times_class; 52 | jmethodID cpu_times_constructor; 53 | jobject cpu_times; 54 | 55 | idleticks = systicks = userticks = 0; 56 | kc = kstat_open(); 57 | for (i = 0; i < num_cpus; i++) { 58 | // the next line is wrong: should replace "cpu_stat0" with "cpu_stati" 59 | // but my C-fu is insufficient to work out how to do this in <10 lines 60 | if ((ksp = kstat_lookup(kc, "cpu_stat", -1, "cpu_stat0")) != NULL) { 61 | kstat_read(kc, ksp, &cpu_stats); 62 | idleticks += cpu_stats.cpu_sysinfo.cpu[CPU_IDLE]; 63 | userticks += cpu_stats.cpu_sysinfo.cpu[CPU_USER]; 64 | systicks += cpu_stats.cpu_sysinfo.cpu[CPU_KERNEL]; 65 | systicks += cpu_stats.cpu_sysinfo.cpu[CPU_WAIT]; 66 | } 67 | } 68 | 69 | cpu_times_class = (*env)->FindClass(env, "com/jezhumble/javasysmon/CpuTimes"); 70 | cpu_times_constructor = (*env)->GetMethodID(env, cpu_times_class, "", "(JJJ)V"); 71 | cpu_times = (*env)->NewObject(env, cpu_times_class, cpu_times_constructor, (jlong) userticks, (jlong) systicks, (jlong) idleticks); 72 | (*env)->DeleteLocalRef(env, cpu_times_class); 73 | return cpu_times; 74 | } 75 | 76 | 77 | JNIEXPORT jobject JNICALL Java_com_jezhumble_javasysmon_SolarisMonitor_physical (JNIEnv *env, jobject obj) 78 | { 79 | jclass memory_stats_class; 80 | jmethodID memory_stats_constructor; 81 | jobject memory_stats; 82 | unsigned long long free_mem; 83 | 84 | free_mem = sysconf(_SC_AVPHYS_PAGES) * pagesize; 85 | memory_stats_class = (*env)->FindClass(env, "com/jezhumble/javasysmon/MemoryStats"); 86 | memory_stats_constructor = (*env)->GetMethodID(env, memory_stats_class, "", "(JJ)V"); 87 | memory_stats = (*env)->NewObject(env, memory_stats_class, memory_stats_constructor, (jlong) free_mem, (jlong) phys_mem); 88 | (*env)->DeleteLocalRef(env, memory_stats_class); 89 | return memory_stats; 90 | } 91 | 92 | int get_swap_stats(unsigned long long *total_swap, unsigned long long *free_swap) 93 | { 94 | swaptbl_t *swaptbl; 95 | swapent_t *swapent; 96 | int i, n, num_entries; 97 | char *strtab; 98 | 99 | n = num_entries = i = 0; 100 | again: 101 | if ((num_entries = swapctl(SC_GETNSWP, NULL)) == -1) { 102 | perror("swapctl: GETNSWP"); 103 | return 1; 104 | } 105 | if (num_entries == 0) { 106 | fprintf(stderr, "No swap devices configures\n"); 107 | return 2; 108 | } 109 | if ((swaptbl = (swaptbl_t *) malloc(num_entries * sizeof(swapent_t) + 110 | sizeof(struct swaptable))) == (void *) 0) { 111 | fprintf(stderr, "Malloc failed\n"); 112 | return 3; 113 | } 114 | /* allocate num+1 string holders */ 115 | if ((strtab = (char *) malloc((num_entries + 1) * MAXSTRSIZE)) == (void *) 0) { 116 | free(swaptbl); 117 | fprintf(stderr, "Malloc Failed\n"); 118 | return 4; 119 | } 120 | /* initialize string pointers */ 121 | for (i = 0; i < (num_entries + 1); i++) { 122 | swaptbl->swt_ent[i].ste_path = strtab + (i * MAXSTRSIZE); 123 | } 124 | swaptbl->swt_n = num_entries + 1; 125 | if ((n = swapctl(SC_LIST, swaptbl)) < 0) { 126 | perror("swapctl"); 127 | free(swaptbl); 128 | free(strtab); 129 | return 5; 130 | } 131 | if (n > num_entries) { 132 | free(swaptbl); 133 | free(strtab); 134 | goto again; 135 | } 136 | for (i = 0; i < n; i++) { 137 | *total_swap += swaptbl->swt_ent[i].ste_pages * pagesize; 138 | *free_swap += swaptbl->swt_ent[i].ste_free * pagesize; 139 | } 140 | free(swaptbl); 141 | free(strtab); 142 | } 143 | 144 | JNIEXPORT jobject JNICALL Java_com_jezhumble_javasysmon_SolarisMonitor_swap (JNIEnv *env, jobject obj) 145 | { 146 | jclass memory_stats_class; 147 | jmethodID memory_stats_constructor; 148 | jobject memory_stats; 149 | unsigned long long total_swap, free_swap; 150 | total_swap = free_swap = 0; 151 | get_swap_stats(&total_swap, &free_swap); 152 | 153 | memory_stats_class = (*env)->FindClass(env, "com/jezhumble/javasysmon/MemoryStats"); 154 | memory_stats_constructor = (*env)->GetMethodID(env, memory_stats_class, "", "(JJ)V"); 155 | memory_stats = (*env)->NewObject(env, memory_stats_class, memory_stats_constructor, (jlong) free_swap, (jlong) total_swap); 156 | (*env)->DeleteLocalRef(env, memory_stats_class); 157 | return memory_stats; 158 | } 159 | 160 | JNIEXPORT jint JNICALL Java_com_jezhumble_javasysmon_SolarisMonitor_numCpus (JNIEnv *env, jobject obj) 161 | { 162 | return (jint) num_cpus; 163 | } 164 | 165 | JNIEXPORT jlong JNICALL Java_com_jezhumble_javasysmon_SolarisMonitor_cpuFrequencyInHz (JNIEnv *env, jobject obj) 166 | { 167 | kstat_ctl_t *kc; 168 | kstat_t *ksp; 169 | kstat_named_t *knp; 170 | 171 | kc = kstat_open(); 172 | if ((ksp = kstat_lookup(kc, "cpu_info", -1, NULL)) == NULL) { 173 | fprintf(stderr, "%s\n", "ERROR: Can't read cpu frequency."); 174 | return 0; 175 | } 176 | if ((kstat_read(kc, ksp, NULL) != -1) && 177 | /* lookup the CPU speed data record */ 178 | ((knp = kstat_data_lookup(ksp, "clock_MHz")) != NULL)) { 179 | return (jlong) 1000 * 1000 * knp->value.ui64; 180 | } else { 181 | return 0; 182 | } 183 | } 184 | 185 | JNIEXPORT jlong JNICALL Java_com_jezhumble_javasysmon_SolarisMonitor_uptimeInSeconds (JNIEnv *env, jobject obj) 186 | { 187 | struct timeval secs; 188 | kstat_ctl_t *kc; 189 | kstat_t *ksp; 190 | kstat_named_t *knp; 191 | unsigned long long uptime; 192 | 193 | if (gettimeofday(&secs, NULL) != 0) { 194 | return (jlong) 0; 195 | } 196 | uptime = (unsigned long long) secs.tv_sec; 197 | 198 | kc = kstat_open(); 199 | if ((ksp = kstat_lookup(kc, "unix", 0, "system_misc")) == NULL) { 200 | fprintf(stderr, "%s\n", "ERROR: Can't read boot time."); 201 | return 0; 202 | } 203 | if ((kstat_read(kc, ksp, NULL) != -1) && 204 | /* lookup the boot time record */ 205 | ((knp = kstat_data_lookup(ksp, "boot_time")) != NULL)) { 206 | return (jlong) (uptime - knp->value.ui32); 207 | } else { 208 | return 0; 209 | } 210 | } 211 | 212 | JNIEXPORT jint JNICALL Java_com_jezhumble_javasysmon_SolarisMonitor_currentPid (JNIEnv *env, jobject obj) 213 | { 214 | return (jint) getpid(); 215 | } 216 | 217 | JNIEXPORT jobject JNICALL Java_com_jezhumble_javasysmon_SolarisMonitor_psinfoToProcess (JNIEnv *env, jobject object, jbyteArray psinfo, jbyteArray prusage) 218 | { 219 | jclass process_info_class; 220 | jmethodID process_info_constructor; 221 | jobject process_info; 222 | psinfo_t *info; 223 | prusage_t *usage; 224 | struct passwd *user; 225 | 226 | process_info_class = (*env)->FindClass(env, "com/jezhumble/javasysmon/ProcessInfo"); 227 | process_info_constructor = (*env)->GetMethodID(env, process_info_class, "", 228 | "(IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;JJJJ)V"); 229 | info = (psinfo_t*) (*env)->GetByteArrayElements(env, psinfo, NULL); 230 | usage = (prusage_t*) (*env)->GetByteArrayElements(env, prusage, NULL); 231 | user = getpwuid(info->pr_uid); 232 | // when somebody wants to get the command line, the trick is to get info->pr_argc (argument count) 233 | // and info->pr_argv (pointer to initial argument vector) and use it as an offset into /proc//as 234 | process_info = (*env)->NewObject(env, process_info_class, process_info_constructor, 235 | (jint) info->pr_pid, 236 | (jint) info->pr_ppid, 237 | (*env)->NewStringUTF(env, ""), 238 | (*env)->NewStringUTF(env, info->pr_fname), 239 | (*env)->NewStringUTF(env, user->pw_name), 240 | (jlong) timespec_to_millisecs(usage->pr_utime), 241 | (jlong) timespec_to_millisecs(usage->pr_stime), 242 | (jlong) info->pr_rssize * 1024, 243 | (jlong) info->pr_size * 1024); 244 | (*env)->ReleaseByteArrayElements(env, psinfo, (jbyte*) info, 0); 245 | (*env)->ReleaseByteArrayElements(env, prusage, (jbyte*) usage, 0); 246 | (*env)->DeleteLocalRef(env, process_info_class); 247 | return process_info; 248 | } 249 | 250 | JNIEXPORT void JNICALL Java_com_jezhumble_javasysmon_SolarisMonitor_killProcess (JNIEnv *env, jobject object, jint pid) { 251 | kill(pid, SIGTERM); 252 | } 253 | -------------------------------------------------------------------------------- /src/main/c/solaris/javasysmon.h: -------------------------------------------------------------------------------- 1 | /* DO NOT EDIT THIS FILE - it is machine generated */ 2 | #include 3 | /* Header for class com_jezhumble_javasysmon_SolarisMonitor */ 4 | 5 | #ifndef _Included_com_jezhumble_javasysmon_SolarisMonitor 6 | #define _Included_com_jezhumble_javasysmon_SolarisMonitor 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | /* 12 | * Class: com_jezhumble_javasysmon_SolarisMonitor 13 | * Method: numCpus 14 | * Signature: ()I 15 | */ 16 | JNIEXPORT jint JNICALL Java_com_jezhumble_javasysmon_SolarisMonitor_numCpus 17 | (JNIEnv *, jobject); 18 | 19 | /* 20 | * Class: com_jezhumble_javasysmon_SolarisMonitor 21 | * Method: cpuFrequencyInHz 22 | * Signature: ()I 23 | */ 24 | JNIEXPORT jlong JNICALL Java_com_jezhumble_javasysmon_SolarisMonitor_cpuFrequencyInHz 25 | (JNIEnv *, jobject); 26 | 27 | /* 28 | * Class: com_jezhumble_javasysmon_SolarisMonitor 29 | * Method: uptimeInSeconds 30 | * Signature: ()J 31 | */ 32 | JNIEXPORT jlong JNICALL Java_com_jezhumble_javasysmon_SolarisMonitor_uptimeInSeconds 33 | (JNIEnv *, jobject); 34 | 35 | /* 36 | * Class: com_jezhumble_javasysmon_SolarisMonitor 37 | * Method: numCpus 38 | * Signature: ()I 39 | */ 40 | JNIEXPORT jint JNICALL Java_com_jezhumble_javasysmon_SolarisMonitor_currentPid 41 | (JNIEnv *, jobject); 42 | 43 | /* 44 | * Class: com_jezhumble_javasysmon_SolarisMonitor 45 | * Method: cpuTimes 46 | * Signature: ()Lcom/jezhumble/javasysmon/CpuTimes; 47 | */ 48 | JNIEXPORT jobject JNICALL Java_com_jezhumble_javasysmon_SolarisMonitor_cpuTimes 49 | (JNIEnv *, jobject); 50 | 51 | /* 52 | * Class: com_jezhumble_javasysmon_SolarisMonitor 53 | * Method: physical 54 | * Signature: ()Lcom/jezhumble/javasysmon/MemoryStats; 55 | */ 56 | JNIEXPORT jobject JNICALL Java_com_jezhumble_javasysmon_SolarisMonitor_physical 57 | (JNIEnv *, jobject); 58 | 59 | /* 60 | * Class: com_jezhumble_javasysmon_SolarisMonitor 61 | * Method: swap 62 | * Signature: ()Lcom/jezhumble/javasysmon/MemoryStats; 63 | */ 64 | JNIEXPORT jobject JNICALL Java_com_jezhumble_javasysmon_SolarisMonitor_swap 65 | (JNIEnv *, jobject); 66 | 67 | /* 68 | * Class: com_jezhumble_javasysmon_SolarisMonitor 69 | * Method: killProcess 70 | * Signature: (I)V 71 | */ 72 | JNIEXPORT void JNICALL Java_com_jezhumble_javasysmon_SolarisMonitor_killProcess 73 | (JNIEnv *, jobject, jint); 74 | 75 | /* 76 | * Class: com_jezhumble_javasysmon_SolarisMonitor 77 | * Method: psinfoToProcess 78 | * Signature: ([B[B)Lcom/jezhumble/javasysmon/ProcessInfo; 79 | */ 80 | JNIEXPORT jobject JNICALL Java_com_jezhumble_javasysmon_SolarisMonitor_psinfoToProcess 81 | (JNIEnv *, jobject, jbyteArray, jbyteArray); 82 | 83 | #ifdef __cplusplus 84 | } 85 | #endif 86 | #endif 87 | -------------------------------------------------------------------------------- /src/main/c/windows/Psapi.Lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jezhumble/javasysmon/4caef77a5b071abb6662eaa07acf376821277112/src/main/c/windows/Psapi.Lib -------------------------------------------------------------------------------- /src/main/c/windows/javasysmon.c: -------------------------------------------------------------------------------- 1 | /* 2 | * javasysmon.c 3 | * javanativetools 4 | * 5 | * Created by Jez Humble on 15 Dec 2009. 6 | * Copyright 2009 Jez Humble. All rights reserved. 7 | * Licensed under the terms of the New BSD license. 8 | * TODO: Error checking 9 | */ 10 | #define _WIN64_WINNT 0x0501 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | static SYSTEM_INFO system_info; 20 | static int num_cpu; 21 | static DWORD current_pid; 22 | static ULONGLONG cpu_frequency; 23 | 24 | static ULONGLONG filetime_to_millis (FILETIME* filetime) 25 | { 26 | ULARGE_INTEGER time; 27 | 28 | time.LowPart = filetime->dwLowDateTime; 29 | time.HighPart = filetime->dwHighDateTime; 30 | 31 | return time.QuadPart / 10000; 32 | } 33 | 34 | JNIEXPORT jint JNICALL JNI_OnLoad (JavaVM * vm, void * reserved) 35 | { 36 | DWORD dwValue; 37 | HKEY hKey; 38 | DWORD dwType=REG_DWORD; 39 | DWORD dwSize=sizeof(DWORD); 40 | 41 | // Get some system information that won't change that we'll need later on 42 | GetSystemInfo (&system_info); 43 | num_cpu = system_info.dwNumberOfProcessors; 44 | 45 | current_pid = GetCurrentProcessId(); 46 | 47 | if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, 48 | "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", 49 | 0L, KEY_QUERY_VALUE, &hKey) == 0) { 50 | if (RegQueryValueEx(hKey, "~MHz", NULL, &dwType,(LPBYTE)&dwValue, &dwSize) == 0) { 51 | cpu_frequency = 1000 * 1000 * dwValue; 52 | } 53 | RegCloseKey(hKey); 54 | } 55 | 56 | return JNI_VERSION_1_2; 57 | } 58 | 59 | JNIEXPORT jobject JNICALL Java_com_jezhumble_javasysmon_WindowsMonitor_cpuTimes (JNIEnv *env, jobject obj) 60 | { 61 | ULONGLONG idle, kernel, user; 62 | FILETIME idletime, kerneltime, usertime; 63 | jclass cpu_times_class; 64 | jmethodID cpu_times_constructor; 65 | jobject cpu_times; 66 | 67 | GetSystemTimes(&idletime, &kerneltime, &usertime); 68 | idle = filetime_to_millis(&idletime); 69 | kernel = filetime_to_millis(&kerneltime) - idle; 70 | user = filetime_to_millis(&usertime); 71 | 72 | cpu_times_class = (*env)->FindClass(env, "com/jezhumble/javasysmon/CpuTimes"); 73 | cpu_times_constructor = (*env)->GetMethodID(env, cpu_times_class, "", "(JJJ)V"); 74 | cpu_times = (*env)->NewObject(env, cpu_times_class, cpu_times_constructor, (jlong) user, (jlong) kernel, (jlong) idle); 75 | (*env)->DeleteLocalRef(env, cpu_times_class); 76 | return cpu_times; 77 | } 78 | 79 | JNIEXPORT jobject JNICALL Java_com_jezhumble_javasysmon_WindowsMonitor_physical (JNIEnv *env, jobject obj) 80 | { 81 | PERFORMANCE_INFORMATION perfinfo; 82 | jclass memory_stats_class; 83 | jmethodID memory_stats_constructor; 84 | jobject memory_stats; 85 | DWORD pagesize; 86 | 87 | GetPerformanceInfo (&perfinfo, sizeof(perfinfo)); 88 | pagesize = perfinfo.PageSize; 89 | memory_stats_class = (*env)->FindClass(env, "com/jezhumble/javasysmon/MemoryStats"); 90 | memory_stats_constructor = (*env)->GetMethodID(env, memory_stats_class, "", "(JJ)V"); 91 | memory_stats = (*env)->NewObject(env, memory_stats_class, memory_stats_constructor, 92 | (jlong) (pagesize * perfinfo.PhysicalAvailable), 93 | (jlong) (pagesize * perfinfo.PhysicalTotal)); 94 | (*env)->DeleteLocalRef(env, memory_stats_class); 95 | return memory_stats; 96 | } 97 | 98 | JNIEXPORT jobject JNICALL Java_com_jezhumble_javasysmon_WindowsMonitor_swap (JNIEnv *env, jobject obj) 99 | { 100 | PERFORMANCE_INFORMATION perfinfo; 101 | jclass memory_stats_class; 102 | jmethodID memory_stats_constructor; 103 | jobject memory_stats; 104 | DWORD pagesize; 105 | 106 | GetPerformanceInfo (&perfinfo, sizeof(perfinfo)); 107 | pagesize = perfinfo.PageSize; 108 | memory_stats_class = (*env)->FindClass(env, "com/jezhumble/javasysmon/MemoryStats"); 109 | memory_stats_constructor = (*env)->GetMethodID(env, memory_stats_class, "", "(JJ)V"); 110 | memory_stats = (*env)->NewObject(env, memory_stats_class, memory_stats_constructor, 111 | (jlong) (pagesize * perfinfo.CommitTotal), 112 | (jlong) (pagesize * perfinfo.CommitLimit)); 113 | (*env)->DeleteLocalRef(env, memory_stats_class); 114 | return memory_stats; 115 | } 116 | 117 | JNIEXPORT jint JNICALL Java_com_jezhumble_javasysmon_WindowsMonitor_numCpus (JNIEnv *env, jobject object) 118 | { 119 | return (jint) num_cpu; 120 | } 121 | 122 | JNIEXPORT jlong JNICALL Java_com_jezhumble_javasysmon_WindowsMonitor_cpuFrequencyInHz (JNIEnv *env, jobject object) 123 | { 124 | return (jlong) cpu_frequency; 125 | } 126 | 127 | JNIEXPORT jlong JNICALL Java_com_jezhumble_javasysmon_WindowsMonitor_uptimeInSeconds (JNIEnv *env, jobject object) 128 | { 129 | // only works up to 49.7 days. There's GetTickCount64 for Vista / Server 2008. 130 | return (jlong) GetTickCount() / 1000; 131 | } 132 | 133 | JNIEXPORT jint JNICALL Java_com_jezhumble_javasysmon_WindowsMonitor_currentPid (JNIEnv *env, jobject object) 134 | { 135 | return (jint) current_pid; 136 | } 137 | 138 | /** 139 | * Converts the given UTF-16 string to UTF-8. Returns ZERO upon success, NON-ZERO otherwise. 140 | * Upon success, the parameter utf8 points to the UTF-8 string. It is the calling function's responsibility that this resource is freed. 141 | * Upon error the parameter utf8 will be set to NULL. 142 | */ 143 | DWORD WideCharToUTF8(wchar_t* utf16, char** utf8, DWORD* out_utf8_len) 144 | { 145 | int utf8_length; 146 | 147 | *utf8 = NULL; 148 | *out_utf8_len = 0; 149 | 150 | utf8_length = WideCharToMultiByte( 151 | CP_UTF8, // Convert to UTF-8 152 | 0, // No special character conversions required 153 | // (UTF-16 and UTF-8 support the same characters) 154 | utf16, // UTF-16 string to convert 155 | -1, // utf16 is NULL terminated (if not, use length) 156 | NULL, // Determining correct output buffer size 157 | 0, // Determining correct output buffer size 158 | NULL, // Must be NULL for CP_UTF8 159 | NULL); // Must be NULL for CP_UTF8 160 | 161 | if (utf8_length == 0) { 162 | // Error - call GetLastError for details 163 | return GetLastError(); 164 | } 165 | 166 | *utf8 = (char*)malloc(sizeof(char) * utf8_length); // Allocate space for UTF-8 string 167 | 168 | utf8_length = WideCharToMultiByte( 169 | CP_UTF8, // Convert to UTF-8 170 | 0, // No special character conversions required 171 | // (UTF-16 and UTF-8 support the same characters) 172 | utf16, // UTF-16 string to convert 173 | -1, // utf16 is NULL terminated (if not, use length) 174 | *utf8, // UTF-8 output buffer 175 | utf8_length, // UTF-8 output buffer size 176 | NULL, // Must be NULL for CP_UTF8 177 | NULL); // Must be NULL for CP_UTF8 178 | 179 | if (utf8_length == 0) { 180 | // Error - call GetLastError for details 181 | free(*utf8); 182 | *utf8 = NULL; 183 | return GetLastError(); 184 | } 185 | 186 | *out_utf8_len = utf8_length; 187 | 188 | return 0; 189 | } 190 | 191 | typedef struct _LSA_UNICODE_STRING { 192 | USHORT Length; 193 | USHORT MaximumLength; 194 | PWSTR Buffer; 195 | } LSA_UNICODE_STRING, *PLSA_UNICODE_STRING, UNICODE_STRING, *PUNICODE_STRING; 196 | 197 | typedef NTSTATUS (NTAPI *_NtQueryInformationProcess)( 198 | HANDLE ProcessHandle, 199 | DWORD ProcessInformationClass, 200 | PVOID ProcessInformation, 201 | DWORD ProcessInformationLength, 202 | PDWORD ReturnLength 203 | ); 204 | 205 | typedef struct _PROCESS_BASIC_INFORMATION 206 | { 207 | LONG ExitStatus; 208 | PVOID PebBaseAddress; 209 | ULONG_PTR AffinityMask; 210 | LONG BasePriority; 211 | ULONG_PTR UniqueProcessId; 212 | ULONG_PTR ParentProcessId; 213 | } PROCESS_BASIC_INFORMATION, *PPROCESS_BASIC_INFORMATION; 214 | 215 | /** 216 | * Retrieve the original command line from the process environment block (PEB). Returns ZERO upon success, NON-ZERO otherwise. 217 | * Upon success, the parameter commandLine points to the original command line string (UTF-16) that was used to start the given process. 218 | * It is the calling function's responsibility that this resource is freed. 219 | * Upon error the parameter commandLine will be set to NULL. 220 | */ 221 | DWORD GetCommandLineFromPeb(DWORD dwPid, wchar_t** commandLine) 222 | { 223 | DWORD dw; 224 | #ifdef _WIN64 225 | SIZE_T read; 226 | #else 227 | DWORD read; 228 | #endif 229 | HANDLE hProcess; 230 | _NtQueryInformationProcess pNtQip; 231 | PROCESS_BASIC_INFORMATION pbInfo; 232 | UNICODE_STRING cmdline; 233 | WCHAR* wcmdLine; 234 | 235 | *commandLine = NULL; 236 | 237 | hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwPid ); 238 | if( !hProcess ) { 239 | return GetLastError(); 240 | } 241 | 242 | pNtQip = (_NtQueryInformationProcess) GetProcAddress(GetModuleHandleA("ntdll.dll"), 243 | "NtQueryInformationProcess"); 244 | if(!pNtQip) { 245 | CloseHandle(hProcess); 246 | return GetLastError(); 247 | } 248 | 249 | pNtQip(hProcess, 0, &pbInfo, sizeof(pbInfo), NULL); 250 | 251 | #ifdef _WIN64 252 | ReadProcessMemory(hProcess, (PCHAR)(pbInfo.PebBaseAddress) + 0x20, &dw, sizeof(dw), &read); 253 | #else 254 | ReadProcessMemory(hProcess, (PCHAR)(pbInfo.PebBaseAddress) + 0x10, &dw, sizeof(dw), &read); 255 | #endif 256 | 257 | #ifdef _WIN64 258 | ReadProcessMemory(hProcess, (PCHAR)dw+112, &cmdline, sizeof(cmdline), &read); 259 | #else 260 | ReadProcessMemory(hProcess, (PCHAR)dw+64, &cmdline, sizeof(cmdline), &read); 261 | #endif 262 | 263 | wcmdLine = (WCHAR *)malloc(sizeof(char)*(cmdline.Length + 2)); 264 | if( !wcmdLine ) { 265 | CloseHandle(hProcess); 266 | return -1; 267 | } 268 | 269 | ReadProcessMemory(hProcess, (PVOID)cmdline.Buffer, wcmdLine, 270 | cmdline.Length+2, &read); 271 | 272 | *commandLine = wcmdLine; 273 | 274 | CloseHandle(hProcess); 275 | 276 | return 0; 277 | } 278 | 279 | /** 280 | * Retrieve the original command line from the process environment block (PEB). Returns ZERO upon success, NON-ZERO otherwise. 281 | * 282 | * This function is similar to GetCommandLineFromPeb, but returns an UTF-8 string instead. 283 | */ 284 | DWORD GetCommandLineUTF8(DWORD dwPid, char** utf8CommandLine, DWORD* process_command_len) { 285 | wchar_t* wcCommandLine; 286 | 287 | if(!GetCommandLineFromPeb(dwPid, &wcCommandLine)) { 288 | if(!WideCharToUTF8(wcCommandLine, utf8CommandLine, process_command_len)) { 289 | free(wcCommandLine); wcCommandLine = NULL; 290 | return 0; 291 | } else { 292 | free(wcCommandLine); wcCommandLine = NULL; 293 | } 294 | } 295 | 296 | return GetLastError(); 297 | } 298 | 299 | JNIEXPORT jobjectArray JNICALL Java_com_jezhumble_javasysmon_WindowsMonitor_processTable (JNIEnv *env, jobject object) 300 | { 301 | jclass process_info_class; 302 | jmethodID process_info_constructor; 303 | jobject process_info; 304 | jobjectArray process_info_array; 305 | DWORD processes[1024], buffer_size, count; 306 | SIZE_T working_set_size, pagefile_usage; 307 | unsigned int i, ppid; 308 | TCHAR process_name[MAX_PATH] = TEXT(""); 309 | DWORD process_name_len = 0; 310 | TCHAR process_command[MAX_PATH+1] = TEXT(""); 311 | char* process_command_raw; 312 | DWORD process_command_len; 313 | TCHAR user_name[MAX_PATH] = TEXT(""); 314 | TCHAR domain_name[MAX_PATH] = TEXT(""); 315 | FILETIME created, exit, kernel, user; 316 | HANDLE process, snapshot, token; 317 | PTOKEN_USER user_token; 318 | HMODULE module; 319 | PROCESS_MEMORY_COUNTERS pmc; 320 | PROCESSENTRY32 process_entry; 321 | SID_NAME_USE sid_name_use; 322 | 323 | snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 324 | process_entry.dwSize = sizeof(PROCESSENTRY32); 325 | if (!EnumProcesses(processes, sizeof(processes), &buffer_size)) 326 | return NULL; 327 | count = buffer_size / sizeof(DWORD); 328 | process_info_array = (*env)->NewObjectArray(env, count, 329 | (*env)->FindClass(env, "com/jezhumble/javasysmon/ProcessInfo"), NULL); 330 | 331 | for (i = 0; i MAX_PATH) { 357 | process_command_len = MAX_PATH; 358 | } 359 | memcpy(process_command, process_command_raw, process_command_len); 360 | free(process_command_raw); process_command_raw = 0; 361 | } 362 | // get CPU usage 363 | GetProcessTimes(process, &created, &exit, &kernel, &user); 364 | // get owner (thanks to http://www.codeproject.com/KB/cs/processownersid.aspx) 365 | if (OpenProcessToken(process, TOKEN_QUERY, &token)) { 366 | GetTokenInformation(token, TokenUser, (LPVOID) user_token, 0, &buffer_size); 367 | if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { 368 | goto cleanup; 369 | } 370 | user_token = (PTOKEN_USER) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, buffer_size); 371 | if (user_token == NULL) { 372 | goto cleanup; 373 | } 374 | if (!GetTokenInformation(token, TokenUser, (LPVOID) user_token, buffer_size, &buffer_size)) { 375 | goto cleanup; 376 | } 377 | if (!LookupAccountSid(NULL, user_token->User.Sid, user_name, &buffer_size, domain_name, &buffer_size, &sid_name_use)) { 378 | goto cleanup; 379 | } 380 | strcat(domain_name, "\\"); 381 | strcat(domain_name, user_name); 382 | cleanup: 383 | CloseHandle(token); 384 | if (user_token != NULL) { 385 | HeapFree(GetProcessHeap(), 0, (LPVOID) user_token); 386 | } 387 | } 388 | // get memory usage 389 | if (GetProcessMemoryInfo(process, &pmc, sizeof(pmc))) { 390 | working_set_size = pmc.WorkingSetSize; 391 | pagefile_usage = pmc.PagefileUsage; 392 | } 393 | CloseHandle(process); 394 | } 395 | process_info_class = (*env)->FindClass(env, "com/jezhumble/javasysmon/ProcessInfo"); 396 | process_info_constructor = (*env)->GetMethodID(env, process_info_class, "", 397 | "(IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;JJJJ)V"); 398 | process_info = (*env)->NewObject(env, process_info_class, process_info_constructor, (jint) processes[i], 399 | (jint) ppid, // parent id 400 | (*env)->NewStringUTF(env, process_command_len == 0 ? "" : process_command), // command 401 | (*env)->NewStringUTF(env, process_name_len == 0 ? "" : process_name), // name 402 | (*env)->NewStringUTF(env, user_token == NULL ? "" : domain_name), // owner 403 | (jlong) filetime_to_millis(&user), // user millis 404 | (jlong) filetime_to_millis(&kernel), // system millis 405 | (jlong) working_set_size, // resident bytes 406 | (jlong) working_set_size + pagefile_usage); // total bytes 407 | (*env)->SetObjectArrayElement(env, process_info_array, i, process_info); 408 | (*env)->DeleteLocalRef(env, process_info_class); 409 | } 410 | CloseHandle(snapshot); 411 | return process_info_array; 412 | } 413 | 414 | JNIEXPORT void JNICALL Java_com_jezhumble_javasysmon_WindowsMonitor_killProcess (JNIEnv *env, jobject object, jint pid) { 415 | HANDLE process; 416 | 417 | process = OpenProcess(PROCESS_TERMINATE, FALSE, pid); 418 | if (process != NULL) { 419 | TerminateProcess(process, 1); 420 | CloseHandle(process); 421 | } 422 | } 423 | -------------------------------------------------------------------------------- /src/main/c/windows/javasysmon.h: -------------------------------------------------------------------------------- 1 | /* DO NOT EDIT THIS FILE - it is machine generated */ 2 | #include 3 | /* Header for class com_jezhumble_javasysmon_SolarisMonitor */ 4 | 5 | #ifndef _Included_com_jezhumble_javasysmon_WindowsMonitor 6 | #define _Included_com_jezhumble_javasysmon_WindowsMonitor 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | /* 12 | * Class: com_jezhumble_javasysmon_WindowsMonitor 13 | * Method: numCpus 14 | * Signature: ()I 15 | */ 16 | JNIEXPORT jint JNICALL Java_com_jezhumble_javasysmon_WindowsMonitor_numCpus 17 | (JNIEnv *, jobject); 18 | 19 | /* 20 | * Class: com_jezhumble_javasysmon_WindowsMonitor 21 | * Method: cpuFrequencyInHz 22 | * Signature: ()I 23 | */ 24 | JNIEXPORT jlong JNICALL Java_com_jezhumble_javasysmon_WindowsMonitor_cpuFrequencyInHz 25 | (JNIEnv *, jobject); 26 | 27 | /* 28 | * Class: com_jezhumble_javasysmon_WindowsMonitor 29 | * Method: uptimeInSeconds 30 | * Signature: ()J 31 | */ 32 | JNIEXPORT jlong JNICALL Java_com_jezhumble_javasysmon_WindowsMonitor_uptimeInSeconds 33 | (JNIEnv *, jobject); 34 | 35 | /* 36 | * Class: com_jezhumble_javasysmon_WindowsMonitor 37 | * Method: numCpus 38 | * Signature: ()I 39 | */ 40 | JNIEXPORT jint JNICALL Java_com_jezhumble_javasysmon_WindowsMonitor_currentPid 41 | (JNIEnv *, jobject); 42 | 43 | /* 44 | * Class: com_jezhumble_javasysmon_WindowsMonitor 45 | * Method: cpuTimes 46 | * Signature: ()Lcom/jezhumble/javasysmon/CpuTimes; 47 | */ 48 | JNIEXPORT jobject JNICALL Java_com_jezhumble_javasysmon_WindowsMonitor_cpuTimes 49 | (JNIEnv *, jobject); 50 | 51 | /* 52 | * Class: com_jezhumble_javasysmon_WindowsMonitor 53 | * Method: physical 54 | * Signature: ()Lcom/jezhumble/javasysmon/MemoryStats; 55 | */ 56 | JNIEXPORT jobject JNICALL Java_com_jezhumble_javasysmon_WindowsMonitor_physical 57 | (JNIEnv *, jobject); 58 | 59 | /* 60 | * Class: com_jezhumble_javasysmon_WindowsMonitor 61 | * Method: swap 62 | * Signature: ()Lcom/jezhumble/javasysmon/MemoryStats; 63 | */ 64 | JNIEXPORT jobject JNICALL Java_com_jezhumble_javasysmon_WindowsMonitor_swap 65 | (JNIEnv *, jobject); 66 | 67 | /* 68 | * Class: com_jezhumble_javasysmon_WindowsMonitor 69 | * Method: processTable 70 | * Signature: ()[Lcom/jezhumble/javasysmon/ProcessInfo; 71 | */ 72 | JNIEXPORT jobjectArray JNICALL Java_com_jezhumble_javasysmon_WindowsMonitor_processTable 73 | (JNIEnv *, jobject); 74 | 75 | /* 76 | * Class: com_jezhumble_javasysmon_WindowsMonitor 77 | * Method: killProcess 78 | * Signature: (I)V 79 | */ 80 | JNIEXPORT void JNICALL Java_com_jezhumble_javasysmon_WindowMonitor_killProcess 81 | (JNIEnv *, jobject, jint); 82 | 83 | #ifdef __cplusplus 84 | } 85 | #endif 86 | #endif 87 | -------------------------------------------------------------------------------- /src/main/c/windows/javasysmon.vcproj: -------------------------------------------------------------------------------- 1 |  2 | 10 | 11 | 14 | 17 | 18 | 19 | 20 | 21 | 27 | 30 | 33 | 36 | 39 | 42 | 55 | 58 | 61 | 64 | 73 | 76 | 79 | 82 | 85 | 88 | 91 | 94 | 95 | 101 | 104 | 107 | 110 | 113 | 117 | 130 | 133 | 136 | 139 | 146 | 149 | 153 | 156 | 159 | 162 | 165 | 168 | 169 | 175 | 178 | 181 | 184 | 187 | 190 | 201 | 204 | 207 | 210 | 222 | 225 | 228 | 231 | 234 | 237 | 240 | 243 | 244 | 250 | 253 | 256 | 259 | 262 | 266 | 276 | 279 | 282 | 285 | 295 | 298 | 301 | 304 | 307 | 310 | 313 | 316 | 317 | 318 | 319 | 320 | 321 | 326 | 329 | 330 | 331 | 336 | 337 | 342 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | -------------------------------------------------------------------------------- /src/main/c/windows/x64/Psapi.Lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jezhumble/javasysmon/4caef77a5b071abb6662eaa07acf376821277112/src/main/c/windows/x64/Psapi.Lib -------------------------------------------------------------------------------- /src/main/java/com/jezhumble/javasysmon/CpuTimes.java: -------------------------------------------------------------------------------- 1 | package com.jezhumble.javasysmon; 2 | 3 | /** 4 | * This object represents a snapshot detailing the total time the CPUs have spent 5 | * idle, in user mode, and in kernel mode. 6 | */ 7 | public class CpuTimes { 8 | private final long userMillis; 9 | private final long systemMillis; 10 | private final long idleMillis; 11 | 12 | public CpuTimes(long userMillis, long systemMillis, long idleMillis) { 13 | this.userMillis = userMillis; 14 | this.systemMillis = systemMillis; 15 | this.idleMillis = idleMillis; 16 | } 17 | 18 | /** 19 | * The total time in milliseconds that the CPUs have spent in user mode. 20 | * 21 | * @return The total time in milliseconds that the CPUs have spent in user mode. 22 | */ 23 | public long getUserMillis() { 24 | return userMillis; 25 | } 26 | 27 | /** 28 | * The total time in milliseconds that the CPUs have spent in kernel mode. 29 | * 30 | * @return The total time in milliseconds that the CPUs have spent in kernel mode. 31 | */ 32 | public long getSystemMillis() { 33 | return systemMillis; 34 | } 35 | 36 | /** 37 | * The total time in milliseconds that the CPUs have spent idle. 38 | * 39 | * @return The total time in milliseconds that the CPUs have spent idle. 40 | */ 41 | public long getIdleMillis() { 42 | return idleMillis; 43 | } 44 | 45 | /** 46 | * The total time in milliseconds that the CPUs have been alive since the system 47 | * was last booted. Should equal the sum of the other three numbers. 48 | * 49 | * @return The total time in milliseconds that the CPUs have been alive. 50 | */ 51 | public long getTotalMillis() { 52 | return userMillis + systemMillis + idleMillis; 53 | } 54 | 55 | /** 56 | * Gets the CPU usage given a previous snapshot of CPU times. 57 | * The number returned represents the proportion of time between 58 | * the two snapshots that the CPUs spent not idle. 59 | * 60 | * @param previous a CpuTimes snapshot taken previously. 61 | * @return the proportion of time between the previous snapshot and this snapshot 62 | * that the CPUs have spent working. 1 represents 100% usage, 0 represents 0% usage. 63 | */ 64 | public float getCpuUsage(CpuTimes previous) { 65 | if (getIdleMillis() == previous.getIdleMillis()) { 66 | return 1f; 67 | } 68 | return 1 - ((float) (getIdleMillis() - previous.getIdleMillis())) / 69 | (float) (getTotalMillis() - previous.getTotalMillis()); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/com/jezhumble/javasysmon/FileUtils.java: -------------------------------------------------------------------------------- 1 | package com.jezhumble.javasysmon; 2 | 3 | import java.io.*; 4 | import java.util.regex.Matcher; 5 | import java.util.regex.Pattern; 6 | 7 | /** 8 | * Convenience methods for interacting with the filesystem. 9 | */ 10 | public class FileUtils { 11 | 12 | private static final Pattern PROC_DIR_PATTERN = Pattern.compile("([\\d]*)"); 13 | 14 | private final static FilenameFilter PROCESS_DIRECTORY_FILTER = new FilenameFilter() { 15 | public boolean accept(File dir, String name) { 16 | File fileToTest = new File(dir, name); 17 | return fileToTest.isDirectory() && PROC_DIR_PATTERN.matcher(name).matches(); 18 | } 19 | }; 20 | 21 | /** 22 | * If you're using an operating system that supports the proc filesystem, 23 | * this returns a list of all processes by reading the directories under 24 | * /proc 25 | * 26 | * @return An array of the ids of all processes running on the OS. 27 | */ 28 | public String[] pidsFromProcFilesystem() { 29 | return new File("/proc").list(FileUtils.PROCESS_DIRECTORY_FILTER); 30 | } 31 | 32 | /** 33 | * Given a filename, reads the entire file into a string. 34 | * 35 | * @param fileName The path of the filename to read. Should be absolute. 36 | * @return A string containing the entire contents of the file 37 | * @throws IOException If there's an IO exception while trying to read the file 38 | */ 39 | public String slurp(String fileName) throws IOException { 40 | return slurpFromInputStream(new FileInputStream(fileName)); 41 | } 42 | 43 | /** 44 | * Given a filename, reads the entire file into a byte array. 45 | * 46 | * @param fileName The path of the filename to read. Should be absolute. 47 | * @return A byte array containing the entire contents of the file 48 | * @throws IOException If there's an IO exception while trying to read the file 49 | */ 50 | public byte[] slurpToByteArray(String fileName) throws IOException { 51 | File fileToRead = new File(fileName); 52 | byte[] contents = new byte[(int) fileToRead.length()]; 53 | InputStream inputStream = null; 54 | try { 55 | inputStream = new FileInputStream(fileToRead); 56 | inputStream.read(contents); 57 | return contents; 58 | } finally { 59 | if (inputStream != null) { 60 | inputStream.close(); 61 | } 62 | } 63 | } 64 | 65 | /** 66 | * Given an InputStream, reads the entire file into a string. 67 | * 68 | * @param stream The InputStream representing the file to read 69 | * @return A string containing the entire contents of the input stream 70 | * @throws IOException If there's an IO exception while trying to read the input stream 71 | */ 72 | public String slurpFromInputStream(InputStream stream) throws IOException { 73 | if (stream == null) { 74 | return null; 75 | } 76 | StringWriter sw = new StringWriter(); 77 | String line; 78 | try { 79 | BufferedReader reader = new BufferedReader(new InputStreamReader(stream, "UTF-8")); 80 | while ((line = reader.readLine()) != null) { 81 | sw.write(line); 82 | sw.write('\n'); 83 | } 84 | } finally { 85 | stream.close(); 86 | } 87 | return sw.toString(); 88 | } 89 | 90 | /** 91 | * Runs a regular expression on a file, and returns the first match. 92 | * 93 | * @param pattern The regular expression to use. 94 | * @param filename The path of the filename to match against. Should be absolute. 95 | * @return The first match found. Null if no matches. 96 | */ 97 | public String runRegexOnFile(Pattern pattern, String filename) { 98 | try { 99 | final String file = slurp(filename); 100 | Matcher matcher = pattern.matcher(file); 101 | matcher.find(); 102 | final String firstMatch = matcher.group(1); 103 | if (firstMatch != null && firstMatch.length() > 0) { 104 | return firstMatch; 105 | } 106 | } catch (IOException e) { 107 | // return null to indicate failure 108 | } 109 | return null; 110 | } 111 | 112 | } 113 | -------------------------------------------------------------------------------- /src/main/java/com/jezhumble/javasysmon/JavaSysMon.java: -------------------------------------------------------------------------------- 1 | package com.jezhumble.javasysmon; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Iterator; 5 | 6 | /** 7 | * This class provides the main API for JavaSysMon. 8 | * You must instantiate this class in order to use it, 9 | * but it stores no state, so there is zero overhead to 10 | * instantiating it as many times as you like, and 11 | * hence no need to cache it. 12 | *

13 | * When instantiated for the first time, JavaSysMon 14 | * will discover which operating system it is running on 15 | * and attempt to load the appropriate OS-specific 16 | * extensions. If JavaSysMon doesn't support the OS 17 | * you're running on, all calls to the API will return 18 | * null or zero values. Probably the best one to test is 19 | * osName. 20 | *

21 | * You can run JavaSysMon directly as a jar file, using 22 | * the command "java -jar javasysmon.jar", in which case 23 | * it will display output similar to the UNIX "top" 24 | * command. You can optionally specify a process id as an 25 | * argument, in which case JavaSysMon will attempt to 26 | * kill the process. 27 | * 28 | * @author Jez Humble 29 | */ 30 | public class JavaSysMon implements Monitor { 31 | 32 | private static Monitor monitor = null; 33 | private static ArrayList supported = new ArrayList(); 34 | 35 | /** 36 | * Allows you to register your own implementation of {@link Monitor}. 37 | * 38 | * @param myMonitor An implementation of the Monitor interface that all API calls will be delegated to 39 | */ 40 | public static void setMonitor(Monitor myMonitor) { 41 | if (monitor == null || monitor instanceof NullMonitor) { 42 | monitor = myMonitor; 43 | } 44 | } 45 | 46 | static void addSupportedConfig(String config) { 47 | supported.add(config); 48 | } 49 | 50 | static { 51 | new MacOsXMonitor(); 52 | new LinuxMonitor(); 53 | new WindowsMonitor(); 54 | new SolarisMonitor(); 55 | new NullMonitor(); // make sure the API never gives back a NPE 56 | } 57 | 58 | /** 59 | * Creates a new JavaSysMon object through which to access 60 | * the JavaSysMon API. All necessary state is kept statically 61 | * so there is zero overhead to instantiating this class. 62 | */ 63 | public JavaSysMon() {} 64 | 65 | /** 66 | * This is the main entry point when running the jar directly. 67 | * It prints out some system performance metrics and the process table 68 | * in a format similar to the UNIX top command. Optionally you can 69 | * specify a process id as an argument, in which case JavaSysMon 70 | * will attempt to kill the process specified by that pid. 71 | */ 72 | public static void main (String[] params) throws Exception { 73 | if (monitor instanceof NullMonitor) { 74 | System.err.println("Couldn't find an implementation for OS: " + System.getProperty("os.name")); 75 | System.err.println("Supported configurations:"); 76 | for (Iterator iter = supported.iterator(); iter.hasNext(); ) { 77 | String config = (String) iter.next(); 78 | System.err.println(config); 79 | } 80 | } else { 81 | if (params.length == 1) { 82 | System.out.println("Attempting to kill process id " + params[0]); 83 | monitor.killProcess(Integer.parseInt(params[0])); 84 | } 85 | CpuTimes initialTimes = monitor.cpuTimes(); 86 | System.out.println("OS name: " + monitor.osName() + 87 | " Uptime: " + secsInDaysAndHours(monitor.uptimeInSeconds()) + 88 | " Current PID: " + monitor.currentPid()); 89 | System.out.println("Number of CPUs: " + monitor.numCpus() + 90 | " CPU frequency: " + monitor.cpuFrequencyInHz() / (1000*1000) + " MHz"); 91 | System.out.println("RAM " + monitor.physical() + " SWAP " + monitor.swap()); 92 | System.out.println("Sampling CPU usage..."); 93 | Thread.sleep(500); 94 | System.out.println("CPU Usage: " + monitor.cpuTimes().getCpuUsage(initialTimes)); 95 | System.out.println("\n" + ProcessInfo.header()); 96 | ProcessInfo[] processes = monitor.processTable(); 97 | for (int i = 0; i < processes.length; i++) { 98 | System.out.println(processes[i].toString()); 99 | } 100 | } 101 | } 102 | 103 | /** 104 | * Whether or not JavaSysMon is running on a supported platform. 105 | * 106 | * @return true if the platform is supported, 107 | * false if it isn't. 108 | */ 109 | public boolean supportedPlatform() { 110 | return !(monitor instanceof NullMonitor); 111 | } 112 | 113 | private static String secsInDaysAndHours(long seconds) { 114 | long days = seconds / (60 * 60 * 24); 115 | long hours = (seconds / (60 * 60)) - (days * 24); 116 | return days + " days " + hours + " hours"; 117 | } 118 | 119 | // Following is the actual API 120 | 121 | /** 122 | * Get the operating system name. 123 | * 124 | * @return The operating system name. 125 | */ 126 | public String osName() { 127 | return monitor.osName(); 128 | } 129 | 130 | /** 131 | * Get the number of CPU cores. 132 | * 133 | * @return The number of CPU cores. 134 | */ 135 | public int numCpus() { 136 | return monitor.numCpus(); 137 | } 138 | 139 | /** 140 | * Get the CPU frequency in Hz 141 | * 142 | * @return the CPU frequency in Hz 143 | */ 144 | public long cpuFrequencyInHz() { 145 | return monitor.cpuFrequencyInHz(); 146 | } 147 | 148 | /** 149 | * How long the system has been up in seconds. 150 | * Doesn't generally include time that the system 151 | * has been hibernating or asleep. 152 | * 153 | * @return The time the system has been up in seconds. 154 | */ 155 | public long uptimeInSeconds() { 156 | return monitor.uptimeInSeconds(); 157 | } 158 | 159 | /** 160 | * Gets the pid of the process that is calling this method 161 | * (assuming it is running in the same process). 162 | * 163 | * @return The pid of the process calling this method. 164 | */ 165 | public int currentPid() { 166 | return monitor.currentPid(); 167 | } 168 | 169 | /** 170 | * Gets a snapshot which contains the total amount 171 | * of time the CPU has spent in user mode, kernel mode, 172 | * and idle. Given two snapshots, you can calculate 173 | * the CPU usage during that time. There is a convenience 174 | * method to perform this calculation in 175 | * {@link CpuTimes#getCpuUsage} 176 | * 177 | * @return An object containing the amount of time the 178 | * CPU has spent idle, in user mode and in kernel mode, 179 | * in milliseconds. 180 | */ 181 | public CpuTimes cpuTimes() { 182 | return monitor.cpuTimes(); 183 | } 184 | 185 | /** 186 | * Gets the physical memory installed, and the amount free. 187 | * 188 | * @return An object containing the amount of physical 189 | * memory installed, and the amount free. 190 | */ 191 | public MemoryStats physical() { 192 | return monitor.physical(); 193 | } 194 | 195 | /** 196 | * Gets the amount of swap available to the operating system, 197 | * and the amount that is free. 198 | * 199 | * @return An object containing the amount of swap available 200 | * to the system, and the amount free. 201 | */ 202 | public MemoryStats swap() { 203 | return monitor.swap(); 204 | } 205 | 206 | /** 207 | * Get the current process table. This call returns an array of 208 | * objects, each of which represents a single process. If you want 209 | * the objects in a tree structure, use {@link #processTree} instead. 210 | * 211 | * @return An array of objects, each of which represents a process. 212 | */ 213 | public ProcessInfo[] processTable() { 214 | return monitor.processTable(); 215 | } 216 | 217 | /** 218 | * Gets the current process table in the form of a process tree. 219 | * The object returned is a top-level container which doesn't actually 220 | * represent a process - its children are the top-level processes 221 | * running in the system. This is necessary because some operating systems 222 | * (Windows, for example) don't have a single top-level process (orphans 223 | * are literally orphaned), and because the process table snapshot 224 | * is not atomic. That means the process table thus returned can be 225 | * internally inconsistent. 226 | * 227 | * @return The current process table in the form of a process tree. 228 | */ 229 | public OsProcess processTree() { 230 | return OsProcess.createTree(monitor.processTable()); 231 | } 232 | 233 | /** 234 | * Attempts to kill the process identified by the integer id supplied. 235 | * This will silently fail if you don't have the authority to kill 236 | * that process. This method sends SIGTERM on the UNIX platform, 237 | * and kills the process using TerminateProcess on Windows. 238 | * 239 | * @param pid The id of the process to kill 240 | */ 241 | public void killProcess(int pid) { 242 | monitor.killProcess(pid); 243 | } 244 | 245 | /** 246 | * Allows you to visit the process tree, starting at the node identified by pid. 247 | * The process tree is traversed depth-first. 248 | * 249 | * @param pid The identifier of the node to start visiting 250 | * @param processVisitor The visitor 251 | */ 252 | public void visitProcessTree(final int pid, final ProcessVisitor processVisitor) { 253 | final OsProcess process = processTree().find(pid); 254 | if (process != null) { 255 | process.accept(processVisitor, 0); 256 | } 257 | } 258 | 259 | /** 260 | * Kills the process tree starting at the process identified by pid. The 261 | * process tree is killed from the bottom up to ensure that orphans are 262 | * not generated. 263 | *

264 | * This method uses {@link #visitProcessTree}. 265 | * 266 | * @param pid The identifier of the process at which to start killing the tree. 267 | * @param descendantsOnly Whether or not to kill the process you start at, 268 | * or only its descendants 269 | */ 270 | public void killProcessTree(final int pid, final boolean descendantsOnly) { 271 | visitProcessTree(pid, new ProcessVisitor() { 272 | public boolean visit(OsProcess process, int level) { 273 | return !descendantsOnly || (pid != process.processInfo().getPid()); 274 | } 275 | }); 276 | } 277 | 278 | /** 279 | * Attempts to kill all the descendants of the currently running process. 280 | */ 281 | public void infanticide() { 282 | killProcessTree(currentPid(), true); 283 | } 284 | } 285 | -------------------------------------------------------------------------------- /src/main/java/com/jezhumble/javasysmon/LinuxMonitor.java: -------------------------------------------------------------------------------- 1 | package com.jezhumble.javasysmon; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.lang.reflect.Method; 6 | import java.math.BigDecimal; 7 | import java.util.ArrayList; 8 | import java.util.logging.Level; 9 | import java.util.logging.Logger; 10 | import java.util.regex.Matcher; 11 | import java.util.regex.Pattern; 12 | 13 | // Network stats will come from /proc/net/dev; disk stats will be from /proc/diskstats 14 | class LinuxMonitor implements Monitor { 15 | 16 | private static final Logger LOG = Logger.getLogger(LinuxMonitor.class.getName()); 17 | 18 | private static final Pattern TOTAL_MEMORY_PATTERN = 19 | Pattern.compile("MemTotal:\\s+(\\d+) kB", Pattern.MULTILINE); 20 | private static final Pattern FREE_MEMORY_PATTERN = 21 | Pattern.compile("MemFree:\\s+(\\d+) kB", Pattern.MULTILINE); 22 | private static final Pattern TOTAL_SWAP_PATTERN = 23 | Pattern.compile("SwapTotal:\\s+(\\d+) kB", Pattern.MULTILINE); 24 | private static final Pattern FREE_SWAP_PATTERN = 25 | Pattern.compile("SwapFree:\\s+(\\d+) kB", Pattern.MULTILINE); 26 | private static final Pattern CPU_JIFFIES_PATTERN = 27 | Pattern.compile("cpu\\s+(.*)", Pattern.MULTILINE); 28 | private static final Pattern NUM_CPU_PATTERN = 29 | Pattern.compile("processor\\s+:\\s+(\\d+)", Pattern.MULTILINE); 30 | private static final Pattern CPU_FREQ_PATTERN = 31 | Pattern.compile("model name[^@]*@\\s+([0-9.A-Za-z]*)", Pattern.MULTILINE); 32 | private static final Pattern UPTIME_PATTERN = 33 | Pattern.compile("([\\d]*).*"); 34 | private static final Pattern PID_PATTERN = 35 | Pattern.compile("([\\d]*).*"); 36 | private static final Pattern DISTRIBUTION = 37 | Pattern.compile("DISTRIB_DESCRIPTION=\"(.*)\"", Pattern.MULTILINE); 38 | 39 | private FileUtils fileUtils; 40 | private int userHz = 100; // Shouldn't be hardcoded. See below. 41 | 42 | LinuxMonitor(FileUtils fileUtils) { 43 | this.fileUtils = fileUtils; 44 | } 45 | 46 | public LinuxMonitor() { 47 | fileUtils = new FileUtils(); 48 | JavaSysMon.addSupportedConfig("Linux (only tested with x86)"); 49 | if (System.getProperty("os.name").toLowerCase().startsWith("linux")) { 50 | JavaSysMon.setMonitor(this); 51 | JavaSysMon.addSupportedConfig("Linux (only tested with x86)"); 52 | // In theory, this calculation should return userHz. It doesn't seem to work though. 53 | // long uptimeInSeconds = uptimeInSeconds(); 54 | // previousJiffies = fileUtils.runRegexOnFile(CPU_JIFFIES_PATTERN, "/proc/stat"); 55 | // long uptimeInJiffies = getTotalJiffies(previousJiffies.split("\\s+")); 56 | // userHz = (int) (uptimeInJiffies / uptimeInSeconds); 57 | } 58 | } 59 | 60 | public String osName() { 61 | String distribution = fileUtils.runRegexOnFile(DISTRIBUTION, "/etc/lsb-release"); 62 | if (null == distribution) { 63 | return System.getProperty("os.name"); 64 | } 65 | return distribution; 66 | } 67 | 68 | public MemoryStats physical() { 69 | String totalMemory = fileUtils.runRegexOnFile(TOTAL_MEMORY_PATTERN, "/proc/meminfo"); 70 | long total = Long.parseLong(totalMemory) * 1024; 71 | String freeMemory = fileUtils.runRegexOnFile(FREE_MEMORY_PATTERN, "/proc/meminfo"); 72 | long free = Long.parseLong(freeMemory) * 1024; 73 | return new MemoryStats(free, total); 74 | } 75 | 76 | public MemoryStats swap() { 77 | String totalMemory = fileUtils.runRegexOnFile(TOTAL_SWAP_PATTERN, "/proc/meminfo"); 78 | long total = Long.parseLong(totalMemory) * 1024; 79 | String freeMemory = fileUtils.runRegexOnFile(FREE_SWAP_PATTERN, "/proc/meminfo"); 80 | long free = Long.parseLong(freeMemory) * 1024; 81 | return new MemoryStats(free, total); 82 | } 83 | 84 | public int numCpus() { 85 | int numCpus = 0; 86 | try { 87 | String cpuInfo = fileUtils.slurp("/proc/cpuinfo"); 88 | Matcher matcher = NUM_CPU_PATTERN.matcher(cpuInfo); 89 | while (matcher.find()) { 90 | numCpus++; 91 | } 92 | return numCpus; 93 | } catch (IOException ioe) { 94 | // return nothing 95 | } 96 | return 0; 97 | } 98 | 99 | public long cpuFrequencyInHz() { 100 | String cpuFrequencyAsString = fileUtils.runRegexOnFile(CPU_FREQ_PATTERN, "/proc/cpuinfo"); 101 | int strLen = cpuFrequencyAsString.length(); 102 | BigDecimal cpuFrequency = new BigDecimal(cpuFrequencyAsString.substring(0, strLen - 3)); 103 | long multiplier = getMultiplier(cpuFrequencyAsString.charAt(strLen - 3)); 104 | return cpuFrequency.multiply(new BigDecimal(Long.toString(multiplier))).longValue(); 105 | } 106 | 107 | public long uptimeInSeconds() { 108 | String uptime = fileUtils.runRegexOnFile(UPTIME_PATTERN, "/proc/uptime"); 109 | return Long.parseLong(uptime); 110 | } 111 | 112 | public int currentPid() { 113 | String pid = fileUtils.runRegexOnFile(PID_PATTERN, "/proc/self/stat"); 114 | return Integer.parseInt(pid); 115 | } 116 | 117 | public ProcessInfo[] processTable() { 118 | ArrayList processTable = new ArrayList(); 119 | final String[] pids = fileUtils.pidsFromProcFilesystem(); 120 | for (int i = 0; i < pids.length; i++) { 121 | try { 122 | String stat = fileUtils.slurp("/proc/" + pids[i] + "/stat"); 123 | String status = fileUtils.slurp("/proc/" + pids[i] + "/status"); 124 | String cmdline = fileUtils.slurp("/proc/" + pids[i] + "/cmdline"); 125 | UnixPasswdParser passwdParser = new UnixPasswdParser(); 126 | final LinuxProcessInfoParser parser = new LinuxProcessInfoParser(stat, status, cmdline, passwdParser.parse(), userHz); 127 | processTable.add(parser.parse()); 128 | } catch (ParseException pe) { 129 | // Skip this process, but log a warning for diagnosis. 130 | LOG.log(Level.WARNING, pe.getMessage(), pe); 131 | } catch (IOException ioe) { 132 | // process probably died since we got the process list 133 | } 134 | } 135 | return (ProcessInfo[]) processTable.toArray(new ProcessInfo[processTable.size()]); 136 | } 137 | 138 | public CpuTimes cpuTimes() { 139 | String[] parsedJiffies = fileUtils.runRegexOnFile(CPU_JIFFIES_PATTERN, "/proc/stat").split("\\s+"); 140 | long userJiffies = Long.parseLong(parsedJiffies[0]) + Long.parseLong(parsedJiffies[1]); 141 | long idleJiffies = Long.parseLong(parsedJiffies[3]); 142 | long systemJiffies = Long.parseLong(parsedJiffies[2]); 143 | // this is for Linux >= 2.6 144 | if (parsedJiffies.length > 4) { 145 | for (int i = 4; i < parsedJiffies.length; i++) { 146 | systemJiffies += Long.parseLong(parsedJiffies[i]); 147 | } 148 | } 149 | return new CpuTimes(toMillis(userJiffies), toMillis(systemJiffies), toMillis(idleJiffies)); 150 | } 151 | 152 | public void killProcess(int pid) { 153 | try { 154 | ProcessKiller.DESTROY_PROCESS.invoke(null, new Object[]{new Integer(pid)}); 155 | } catch (Exception e) { 156 | throw new RuntimeException("Could not kill process id " + pid, e); 157 | } 158 | } 159 | 160 | private long getMultiplier(char multiplier) { 161 | switch (multiplier) { 162 | case 'G': 163 | return 1000000000; 164 | case 'M': 165 | return 1000000; 166 | case 'k': 167 | return 1000; 168 | } 169 | return 0; 170 | } 171 | 172 | private long toMillis(long jiffies) { 173 | int multiplier = 1000 / userHz; 174 | return jiffies * multiplier; 175 | } 176 | 177 | // Stole this from Hudson (hudson.util.ProcessTree). It's a hack because it's an undocumented API in the JVM. 178 | // However I can't think of any better way to do this without writing native code for Linux which I want to avoid. 179 | // Wouldn't it be nice if deleting a directory in the proc filesystem killed the process? 180 | private static final class ProcessKiller { 181 | private static Method DESTROY_PROCESS = null; 182 | 183 | static { 184 | try { 185 | Class clazz = Class.forName("java.lang.UNIXProcess"); 186 | DESTROY_PROCESS = clazz.getDeclaredMethod("destroyProcess", new Class[]{int.class}); 187 | DESTROY_PROCESS.setAccessible(true); 188 | } catch (Exception e) { 189 | LinkageError x = new LinkageError("Couldn't get method java.lang.UNIXProcess.destroyProcess(int)"); 190 | x.initCause(e); 191 | } 192 | } 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /src/main/java/com/jezhumble/javasysmon/LinuxProcessInfoParser.java: -------------------------------------------------------------------------------- 1 | package com.jezhumble.javasysmon; 2 | 3 | import java.util.HashMap; 4 | import java.util.regex.Matcher; 5 | import java.util.regex.Pattern; 6 | 7 | class LinuxProcessInfoParser { 8 | private final String stat; 9 | private final String status; 10 | private final String cmdline; 11 | private final HashMap uids; 12 | private final int userHz; 13 | 14 | private static final Pattern STATUS_NAME_MATCHER = 15 | Pattern.compile("Name:\\s+(\\w+)", Pattern.MULTILINE); 16 | private static final Pattern STATUS_UID_MATCHER = 17 | Pattern.compile("Uid:\\s+(\\d+)\\s.*", Pattern.MULTILINE); 18 | private static final Pattern STATUS_VM_SIZE_MATCHER = 19 | Pattern.compile("VmSize:\\s+(\\d+) kB", Pattern.MULTILINE); 20 | private static final Pattern STATUS_VM_RSS_MATCHER = 21 | Pattern.compile("VmRSS:\\s+(\\d+) kB", Pattern.MULTILINE); 22 | 23 | public LinuxProcessInfoParser(String stat, String status, String cmdline, HashMap uids, int userHz) { 24 | this.stat = stat; 25 | this.status = status; 26 | this.cmdline = cmdline; 27 | this.uids = uids; 28 | this.userHz = userHz; 29 | } 30 | 31 | public ProcessInfo parse() throws ParseException { 32 | int openParen = stat.indexOf("("); 33 | int closeParen = stat.lastIndexOf(")"); 34 | if (openParen <= 1 || closeParen < 0 || closeParen > stat.length() - 2) { 35 | throw new ParseException("Stat '" + stat + "' does not include expected parens around process name"); 36 | } 37 | 38 | // Start splitting after close of proc name 39 | String[] statElements = stat.substring(closeParen + 2).split(" "); 40 | if (statElements.length < 13) { 41 | throw new ParseException("Stat '" + stat + "' contains fewer elements than expected"); 42 | } 43 | 44 | String pidStr = stat.substring(0, openParen - 1); 45 | 46 | int pid; 47 | int parentPid; 48 | long userMillis; 49 | long systemMillis; 50 | try 51 | { 52 | pid = Integer.parseInt(pidStr); 53 | parentPid = Integer.parseInt(statElements[1]); 54 | userMillis = Long.parseLong(statElements[11]) * (1000 / userHz); 55 | systemMillis = Long.parseLong(statElements[12]) * (1000 / userHz); 56 | } 57 | catch (NumberFormatException e) 58 | { 59 | throw new ParseException("Unable to parse stat '" + stat + "'"); 60 | } 61 | 62 | long residentBytes; 63 | long totalBytes; 64 | try 65 | { 66 | residentBytes = Long.parseLong(getFirstMatch(STATUS_VM_RSS_MATCHER, status)) * 1024; 67 | totalBytes = Long.parseLong(getFirstMatch(STATUS_VM_SIZE_MATCHER, status)) * 1024; 68 | } 69 | catch (NumberFormatException e) 70 | { 71 | throw new ParseException("Unable to extract memory usage information from status '" + status + "'"); 72 | } 73 | 74 | return new ProcessInfo(pid, 75 | parentPid, 76 | trim(cmdline), 77 | getFirstMatch(STATUS_NAME_MATCHER, status), 78 | (String) uids.get(getFirstMatch(STATUS_UID_MATCHER, status)), 79 | userMillis, 80 | systemMillis, 81 | residentBytes, 82 | totalBytes); 83 | } 84 | 85 | private String trim(String cmdline) { 86 | return cmdline.replace('\000', ' ').replace('\n', ' '); 87 | } 88 | 89 | public String getFirstMatch(Pattern pattern, String string) { 90 | try { 91 | Matcher matcher = pattern.matcher(string); 92 | matcher.find(); 93 | return matcher.group(1); 94 | } catch (Exception e) { 95 | return "0"; 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/com/jezhumble/javasysmon/MacOsXMonitor.java: -------------------------------------------------------------------------------- 1 | package com.jezhumble.javasysmon; 2 | 3 | class MacOsXMonitor implements Monitor { 4 | 5 | private static Monitor monitor = null; 6 | 7 | static { 8 | if (System.getProperty("os.name").toLowerCase().equals("mac os x")) { 9 | new NativeLibraryLoader().loadLibrary("libjavasysmon.jnilib"); 10 | monitor = new MacOsXMonitor(); 11 | } 12 | } 13 | 14 | public MacOsXMonitor() { 15 | JavaSysMon.addSupportedConfig("Mac Os X (PPC, x86, X86_64)"); 16 | if (monitor != null) { 17 | JavaSysMon.setMonitor(monitor); 18 | } 19 | } 20 | 21 | public String osName() { 22 | return System.getProperty("os.name") + " " + System.getProperty("os.version"); 23 | } 24 | 25 | public native int numCpus(); 26 | public native long cpuFrequencyInHz(); 27 | public native long uptimeInSeconds(); 28 | public native int currentPid(); 29 | public native CpuTimes cpuTimes(); 30 | public native MemoryStats physical(); 31 | public native MemoryStats swap(); 32 | public native ProcessInfo[] processTable(); 33 | public native void killProcess(int pid); 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/jezhumble/javasysmon/MemoryStats.java: -------------------------------------------------------------------------------- 1 | package com.jezhumble.javasysmon; 2 | 3 | /** 4 | * This object represents a snapshot detailing the total memory of 5 | * some type (physical or swap) available to the operating system, 6 | * and the amount that is currently free. 7 | */ 8 | public class MemoryStats { 9 | 10 | private final static int ONE_MB = 1024 * 1024; 11 | private final long free; 12 | private final long total; 13 | 14 | public MemoryStats(long free, long total) { 15 | this.free = free; 16 | this.total = total; 17 | } 18 | 19 | /** 20 | * The amount of memory that is currently free, in bytes. 21 | * 22 | * @return The amount of memory that is currently free. 23 | */ 24 | public long getFreeBytes() { 25 | return free; 26 | } 27 | 28 | /** 29 | * The amount of memory that is available to the operating system, 30 | * in bytes. 31 | * 32 | * @return The total amount of memory that is available. 33 | */ 34 | public long getTotalBytes() { 35 | return total; 36 | } 37 | 38 | public String toString() { 39 | return "total: " + total / ONE_MB + "Mb free: " + free / ONE_MB + "Mb"; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/jezhumble/javasysmon/Monitor.java: -------------------------------------------------------------------------------- 1 | package com.jezhumble.javasysmon; 2 | 3 | /** 4 | * This is the interface that needs to be 5 | * implemented for any platform that JavaSysMon 6 | * supports. If you write your own implementation, 7 | * you can test it by injecting it into 8 | * {@link JavaSysMon#setMonitor} 9 | */ 10 | public interface Monitor { 11 | /** 12 | * Get the operating system name. 13 | * 14 | * @return The operating system name. 15 | */ 16 | public String osName(); 17 | 18 | /** 19 | * Get the number of CPU cores. 20 | * 21 | * @return The number of CPU cores. 22 | */ 23 | public int numCpus(); 24 | 25 | /** 26 | * Get the CPU frequency in Hz 27 | * 28 | * @return the CPU frequency in Hz 29 | */ 30 | public long cpuFrequencyInHz(); 31 | 32 | /** 33 | * How long the system has been up in seconds. 34 | * Doesn't generally include time that the system 35 | * has been hibernating or asleep. 36 | * 37 | * @return The time the system has been up in seconds. 38 | */ 39 | public long uptimeInSeconds(); 40 | 41 | /** 42 | * Gets a snapshot which contains the total amount 43 | * of time the CPU has spent in user mode, kernel mode, 44 | * and idle. Given two snapshots, you can calculate 45 | * the CPU usage during that time. There is a convenience 46 | * method to perform this calculation in 47 | * {@link CpuTimes#getCpuUsage} 48 | * 49 | * @return An object containing the amount of time the 50 | * CPU has spent idle, in user mode and in kernel mode, 51 | * in milliseconds. 52 | */ 53 | public CpuTimes cpuTimes(); 54 | 55 | /** 56 | * Gets the physical memory installed, and the amount free. 57 | * 58 | * @return An object containing the amount of physical 59 | * memory installed, and the amount free. 60 | */ 61 | public MemoryStats physical(); 62 | 63 | /** 64 | * Gets the amount of swap available to the operating system, 65 | * and the amount that is free. 66 | * 67 | * @return An object containing the amount of swap available 68 | * to the system, and the amount free. 69 | */ 70 | public MemoryStats swap(); 71 | 72 | /** 73 | * Gets the pid of the process that is calling this method 74 | * (assuming it is running in the same process). 75 | * 76 | * @return The pid of the process calling this method. 77 | */ 78 | public int currentPid(); 79 | 80 | /** 81 | * Get the current process table. This call returns an array of 82 | * objects, each of which represents a single process. 83 | * 84 | * @return An array of objects, each of which represents a process. 85 | */ 86 | ProcessInfo[] processTable(); 87 | 88 | /** 89 | * Attempts to kill the process identified by the integer id supplied. 90 | * This will silently fail if you don't have the authority to kill 91 | * that process. This method sends SIGTERM on the UNIX platform, 92 | * and kills the process using TerminateProcess on Windows. 93 | * 94 | * @param pid The id of the process to kill 95 | */ 96 | public void killProcess(int pid); 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/com/jezhumble/javasysmon/NativeLibraryLoader.java: -------------------------------------------------------------------------------- 1 | package com.jezhumble.javasysmon; 2 | 3 | import java.io.*; 4 | 5 | // This is "optimised" based on the fact we only load each native library once. 6 | class NativeLibraryLoader { 7 | 8 | public static final String JAVA_SYS_MON_TEMP_DIR = "JAVA_SYS_MON_TEMP_DIR"; 9 | 10 | public void loadLibrary(String libraryName) { 11 | try { 12 | InputStream is = this.getClass().getResourceAsStream("/" + libraryName); 13 | File tempNativeLib = getTempFile(libraryName); 14 | FileOutputStream os = new FileOutputStream(tempNativeLib); 15 | copyAndClose(is, os); 16 | System.load(tempNativeLib.getAbsolutePath()); 17 | } catch (IOException ioe) { 18 | throw new RuntimeException("Couldn't load native library " + libraryName, ioe); 19 | } 20 | } 21 | 22 | private void copyAndClose(InputStream is, OutputStream os) throws IOException { 23 | byte[] buffer = new byte[1024]; 24 | while (true) { 25 | int len = is.read(buffer); 26 | if (len < 0) break; 27 | os.write(buffer, 0, len); 28 | } 29 | is.close(); 30 | os.close(); 31 | } 32 | 33 | File getTempFile(String libraryName) throws IOException { 34 | int suffixSeparator = libraryName.lastIndexOf("."); 35 | String suffix = null; 36 | String prefix = libraryName; 37 | if (suffixSeparator >= 0) { 38 | suffix = libraryName.substring(suffixSeparator); 39 | prefix = libraryName.substring(0, suffixSeparator - 1); 40 | } 41 | File tempFile = createTempFile(suffix, prefix); 42 | tempFile.deleteOnExit(); 43 | return tempFile; 44 | } 45 | 46 | private File createTempFile(String suffix, String prefix) throws IOException { 47 | String tempDirProp = System.getProperty(JAVA_SYS_MON_TEMP_DIR); 48 | if (tempDirProp == null || tempDirProp.trim().length() == 0) { 49 | return File.createTempFile(prefix, suffix); 50 | } 51 | return File.createTempFile(prefix, suffix, new File(tempDirProp)); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/jezhumble/javasysmon/NullMonitor.java: -------------------------------------------------------------------------------- 1 | package com.jezhumble.javasysmon; 2 | 3 | class NullMonitor implements Monitor { 4 | 5 | public NullMonitor() { 6 | JavaSysMon.setMonitor(this); 7 | } 8 | 9 | public String osName() { 10 | return System.getProperty("os.name"); 11 | } 12 | 13 | public int numCpus() { 14 | return 0; 15 | } 16 | 17 | public long cpuFrequencyInHz() { 18 | return 0; 19 | } 20 | 21 | public CpuTimes cpuTimes() { 22 | return null; 23 | } 24 | 25 | public MemoryStats physical() { 26 | return null; 27 | } 28 | 29 | public MemoryStats swap() { 30 | return null; 31 | } 32 | 33 | public long uptimeInSeconds() { 34 | return 0; 35 | } 36 | 37 | public int currentPid() { 38 | return 0; 39 | } 40 | 41 | public ProcessInfo[] processTable() { 42 | return new ProcessInfo[0]; 43 | } 44 | 45 | public void killProcess(int pid) { 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/jezhumble/javasysmon/OsProcess.java: -------------------------------------------------------------------------------- 1 | package com.jezhumble.javasysmon; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.Iterator; 6 | import java.util.List; 7 | 8 | /** 9 | * This object represents a node in the process tree. It knows 10 | * information about the process it represents, and also 11 | * what its children are. 12 | */ 13 | public class OsProcess { 14 | 15 | private final ArrayList children = new ArrayList(); 16 | private final ProcessInfo processInfo; 17 | 18 | private OsProcess(ProcessInfo processInfo) { 19 | this.processInfo = processInfo; 20 | } 21 | 22 | /** 23 | * This method is the only way to create an OsProcess object. 24 | * It creates a graph of OsProcess objects from an array of 25 | * ProcessInfo objects representing the processes in the system. 26 | * 27 | * @param processTable An array of objects representing the processes in the system. 28 | * @return A graph of OsProcess objects. 29 | */ 30 | public static OsProcess createTree(ProcessInfo[] processTable) { 31 | HashMap processes = new HashMap(); 32 | OsProcess topLevelProcess = new OsProcess(null); 33 | for (int i = 0; i < processTable.length; i++) { 34 | OsProcess process = new OsProcess(processTable[i]); 35 | processes.put(new Integer(processTable[i].getPid()), process); 36 | } 37 | for (int i = 0; i < processTable.length; i++) { 38 | int pid = processTable[i].getPid(); 39 | int ppid = processTable[i].getParentPid(); 40 | OsProcess process = (OsProcess) processes.get(new Integer(pid)); 41 | if (ppid == pid || !processes.containsKey(new Integer(ppid))) { 42 | topLevelProcess.children.add(process); 43 | } else { 44 | ((OsProcess) processes.get(new Integer(ppid))).children.add(process); 45 | } 46 | } 47 | return topLevelProcess; 48 | } 49 | 50 | /** 51 | * Gets the list of child processes of this object. 52 | * 53 | * @return The list of child processes of this object. 54 | */ 55 | public List children() { 56 | return children; 57 | } 58 | 59 | /** 60 | * Information about this process. 61 | * 62 | * @return Information about this process. 63 | */ 64 | public ProcessInfo processInfo() { 65 | return processInfo; 66 | } 67 | 68 | /** 69 | * Finds and returns a particular node in the process tree 70 | * by its id. 71 | * 72 | * @param pid the id of the process to find. 73 | * @return The process node in the tree. 74 | */ 75 | public OsProcess find(int pid) { 76 | if (this.processInfo != null && this.processInfo.getPid() == pid) { 77 | return this; 78 | } 79 | for (Iterator it = children.iterator(); it.hasNext();) { 80 | final OsProcess found = ((OsProcess) it.next()).find(pid); 81 | if (found != null) { 82 | return found; 83 | } 84 | } 85 | return null; 86 | } 87 | 88 | /** 89 | * Method to allow visiting the process tree. Use the convenience method 90 | * {@link JavaSysMon#visitProcessTree} 91 | * 92 | * @param processVisitor An instance of {@link ProcessVisitor} 93 | * @param level The level currently being visited 94 | */ 95 | public void accept(ProcessVisitor processVisitor, int level) { 96 | for (Iterator it = children.iterator(); it.hasNext();) { 97 | ((OsProcess) it.next()).accept(processVisitor, level + 1); 98 | } 99 | if (this.processInfo != null) { 100 | if (processVisitor.visit(this, level)) { 101 | new JavaSysMon().killProcess(processInfo.getPid()); 102 | } 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/main/java/com/jezhumble/javasysmon/ParseException.java: -------------------------------------------------------------------------------- 1 | package com.jezhumble.javasysmon; 2 | 3 | public class ParseException extends RuntimeException { 4 | 5 | public ParseException() { 6 | } 7 | 8 | public ParseException(String s) { 9 | super(s); 10 | } 11 | 12 | public ParseException(String s, Throwable throwable) { 13 | super(s, throwable); 14 | } 15 | 16 | public ParseException(Throwable throwable) { 17 | super(throwable); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/jezhumble/javasysmon/ProcessInfo.java: -------------------------------------------------------------------------------- 1 | package com.jezhumble.javasysmon; 2 | 3 | import java.text.DecimalFormat; 4 | 5 | /** 6 | * This object represents JavaSysMon's understanding of a process. 7 | * You can get all the information JavaSysMon knows about a 8 | * particular process from this object. 9 | *

10 | * There are also convenience methods that can be used to print out 11 | * a process table in a monospace font (for example, on the console). 12 | */ 13 | public class ProcessInfo { 14 | 15 | // Process id info 16 | private int pid; 17 | private int parentPid; 18 | private String command; 19 | private String name; 20 | private String owner; 21 | 22 | // Performance info 23 | private long userMillis; 24 | private long systemMillis; 25 | private long residentBytes; 26 | private long totalBytes; 27 | 28 | public ProcessInfo(int pid, int parentPid, String command, String name, String owner, 29 | long userMillis, long systemMillis, long residentBytes, long totalBytes) { 30 | 31 | this.pid = pid; 32 | this.parentPid = parentPid; 33 | this.command = command; 34 | this.name = name; 35 | this.owner = owner; 36 | this.userMillis = userMillis; 37 | this.systemMillis = systemMillis; 38 | this.residentBytes = residentBytes; 39 | this.totalBytes = totalBytes; 40 | } 41 | 42 | /** 43 | * The id of this process 44 | * 45 | * @return The id of this process 46 | */ 47 | public int getPid() { 48 | return pid; 49 | } 50 | 51 | /** 52 | * The id of the parent process of this parent 53 | * 54 | * @return The id of the parent process of this parent 55 | */ 56 | public int getParentPid() { 57 | return parentPid; 58 | } 59 | 60 | /** 61 | * The command that was originally used to start this process. 62 | * Not currently available on Mac OSX or Solaris (the C source 63 | * contains some information on how to get this data for anyone 64 | * interested in implementing it) 65 | * 66 | * @return A string representing the command that was originally 67 | * used to start this process. 68 | */ 69 | public String getCommand() { 70 | return command; 71 | } 72 | 73 | /** 74 | * The name of this process. This is for display purposes only. 75 | * 76 | * @return The name of this process. 77 | */ 78 | public String getName() { 79 | return name; 80 | } 81 | 82 | /** 83 | * The name of the owner of this process. This is derived from 84 | * the uid, not the effective id. On Windows, this is in the format 85 | * DOMAIN\USERNAME 86 | * 87 | * @return The name of the owner of this process. 88 | */ 89 | public String getOwner() { 90 | return owner; 91 | } 92 | 93 | /** 94 | * The number of milliseconds that this process has been running 95 | * on the CPUs in user mode. Note that this is not "wall time". 96 | * On Mac OSX this information is only available for the current process. 97 | * 98 | * @return The number of milliseconds that this process has been 99 | * running on the CPUs in user mode. 100 | */ 101 | public long getUserMillis() { 102 | return userMillis; 103 | } 104 | 105 | /** 106 | * The number of milliseconds that this process has been running 107 | * on the CPUs in kernel mode. Note that this is not "wall time". 108 | * On Mac OSX this information is only available for the current process. 109 | * 110 | * @return The number of milliseconds that this process has been 111 | * running on the CPUs in kernel mode. 112 | */ 113 | public long getSystemMillis() { 114 | return systemMillis; 115 | } 116 | 117 | /** 118 | * The number of bytes used by this process that are currently in physical 119 | * memory. On Mac OSX this information is only available for the current 120 | * process. 121 | * 122 | * @return The number of bytes used by this process that are currently in 123 | * physical memory. 124 | */ 125 | public long getResidentBytes() { 126 | return residentBytes; 127 | } 128 | 129 | /** 130 | * The total size of this process in bytes. On Mac OSX this information 131 | * is only available for the current process. 132 | * 133 | * @return The total number of bytes used by this process. 134 | */ 135 | public long getTotalBytes() { 136 | return totalBytes; 137 | } 138 | 139 | /** 140 | * Prints out a header that can be used along with {@link #toString} 141 | * (assuming you use a monospace font). 142 | * 143 | * @return A header that can be used when printing out the process table. 144 | */ 145 | public static String header() { 146 | return " pid name ppid user total res time command\n" + 147 | "================================================================================"; 148 | } 149 | 150 | /** 151 | * A one-line string representation of some of the information in this object 152 | * that can be used to print out a single line in a process table. 153 | * Fields have a fixed length so that the table looks nice in a monospace font. 154 | * 155 | * @return a single line representing some of the information about this process. 156 | */ 157 | public String toString() { 158 | // No bloody string formatting in Java 1.4. Starting to reconsider support for it. 159 | // Even C can do this ffs 160 | return stringFormat(pid, 5) + " " + 161 | stringFormat(name, 10) + " " + 162 | stringFormat(parentPid, 5) + " " + 163 | stringFormat(owner, 10) + " " + 164 | stringFormat(totalBytes / (1024 * 1024), 4) + "Mb " + 165 | stringFormat(residentBytes / (1024 * 1024), 4) + "Mb " + 166 | formatMillisecs(userMillis + systemMillis) + " " + 167 | stringFormat(command, 23); 168 | } 169 | 170 | private static String stringFormat(int intToFormat, int fieldSize) { 171 | return stringFormat(Integer.toString(intToFormat), fieldSize, true); 172 | } 173 | 174 | private static String stringFormat(long longToFormat, int fieldSize) { 175 | return stringFormat(Long.toString(longToFormat), fieldSize, true); 176 | } 177 | 178 | private static String stringFormat(String stringToFormat, int fieldSize) { 179 | return stringFormat(stringToFormat, fieldSize, false); 180 | } 181 | 182 | private static String stringFormat(String stringToFormat, int fieldSize, boolean rightJustify) { 183 | // and Java doesn't really excel at this kind of thing either 184 | if (stringToFormat.length() >= fieldSize) { 185 | return stringToFormat.substring(0, fieldSize); 186 | } else { 187 | return rightJustify ? 188 | PADDING.substring(0, fieldSize - stringToFormat.length()) + stringToFormat: 189 | stringToFormat + PADDING.substring(0, fieldSize - stringToFormat.length()); 190 | } 191 | } 192 | 193 | // gotta love this hack 194 | final private static String PADDING = 195 | " "; 196 | 197 | private static String formatMillisecs(long millisecs) { 198 | long secs = millisecs / 1000; 199 | long hours = secs / 3600; 200 | long mins = (secs - (hours * 3600)) / 60; 201 | secs = (secs - (hours * 3600) - (mins * 60)); 202 | DecimalFormat format = new DecimalFormat("00"); 203 | return format.format(hours) + ":" + format.format(mins) + ":" + format.format(secs); 204 | } 205 | } -------------------------------------------------------------------------------- /src/main/java/com/jezhumble/javasysmon/ProcessVisitor.java: -------------------------------------------------------------------------------- 1 | package com.jezhumble.javasysmon; 2 | 3 | /** 4 | * Allows you to visit the process tree using {@link JavaSysMon#visitProcessTree} 5 | */ 6 | public interface ProcessVisitor { 7 | /** 8 | * Called on every node. The process tree is traversed depth-first 9 | * @param process The current process being visited 10 | * @param level How many levels beneath the initial node visited you are (0-indexed) 11 | * @return Whether or not to kill the process being visited 12 | */ 13 | boolean visit(OsProcess process, int level); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/jezhumble/javasysmon/SolarisMonitor.java: -------------------------------------------------------------------------------- 1 | package com.jezhumble.javasysmon; 2 | 3 | import java.io.IOException; 4 | import java.util.ArrayList; 5 | 6 | class SolarisMonitor implements Monitor { 7 | private static Monitor monitor = null; 8 | private final FileUtils fileUtils; 9 | 10 | static { 11 | if (System.getProperty("os.name").toLowerCase().startsWith("sunos")) { 12 | if (System.getProperty("os.arch").toLowerCase().startsWith("x86")) { 13 | new NativeLibraryLoader().loadLibrary("javasysmonsolx86.so"); 14 | monitor = new SolarisMonitor(); 15 | } 16 | } 17 | } 18 | 19 | public SolarisMonitor() { 20 | JavaSysMon.addSupportedConfig("Solaris (x86)"); 21 | if (monitor != null) { 22 | JavaSysMon.setMonitor(monitor); 23 | } 24 | fileUtils = new FileUtils(); 25 | } 26 | 27 | public String osName() { 28 | return System.getProperty("os.name"); 29 | } 30 | 31 | public ProcessInfo[] processTable() { 32 | ArrayList processTable = new ArrayList(); 33 | final String[] pids = fileUtils.pidsFromProcFilesystem(); 34 | for (int i = 0; i < pids.length; i++) { 35 | try { 36 | byte[] psinfo = fileUtils.slurpToByteArray("/proc/" + pids[i] + "/psinfo"); 37 | byte[] usage = fileUtils.slurpToByteArray("/proc/" + pids[i] + "/usage"); 38 | processTable.add(psinfoToProcess(psinfo, usage)); 39 | } catch (IOException e) { 40 | // process doesn't exist any more 41 | } 42 | } 43 | return (ProcessInfo[]) processTable.toArray(new ProcessInfo[processTable.size()]); 44 | } 45 | 46 | public native ProcessInfo psinfoToProcess(byte[] psinfo, byte[] usage); 47 | public native int numCpus(); 48 | public native long cpuFrequencyInHz(); 49 | public native long uptimeInSeconds(); 50 | public native int currentPid(); 51 | public native CpuTimes cpuTimes(); 52 | public native MemoryStats physical(); 53 | public native MemoryStats swap(); 54 | public native void killProcess(int pid); 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/jezhumble/javasysmon/UnixPasswdParser.java: -------------------------------------------------------------------------------- 1 | package com.jezhumble.javasysmon; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.FileInputStream; 5 | import java.io.IOException; 6 | import java.io.InputStreamReader; 7 | import java.util.HashMap; 8 | 9 | class UnixPasswdParser { 10 | 11 | public HashMap parse(BufferedReader reader) { 12 | if (reader == null) { 13 | System.err.println("Error parsing password file: reader is null"); 14 | return new HashMap(); 15 | } 16 | 17 | HashMap users = new HashMap(); 18 | try { 19 | String line; 20 | while ((line = reader.readLine()) != null) { 21 | String[] fields = line.split(":"); 22 | if (fields.length >= 2) { 23 | users.put(fields[2], fields[0]); 24 | } 25 | } 26 | return users; 27 | } catch (IOException e) { 28 | System.err.println("Error parsing password file: " + e.getMessage()); 29 | return new HashMap(); 30 | } finally { 31 | try { 32 | reader.close(); 33 | } catch (IOException e) { 34 | System.err.println("Error closing reader: " + e.getMessage()); 35 | } 36 | } 37 | } 38 | 39 | public HashMap parse() { 40 | try { 41 | final FileInputStream passwdFile = new FileInputStream("/etc/passwd"); 42 | BufferedReader reader = new BufferedReader(new InputStreamReader(passwdFile, "UTF-8")); 43 | return parse(reader); 44 | } catch (IOException e) { 45 | System.err.println("Error reading password file: " + e.getMessage()); 46 | return new HashMap(); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/jezhumble/javasysmon/WindowsMonitor.java: -------------------------------------------------------------------------------- 1 | package com.jezhumble.javasysmon; 2 | 3 | class WindowsMonitor implements Monitor { 4 | private static Monitor monitor = null; 5 | 6 | static { 7 | if (System.getProperty("os.name").toLowerCase().startsWith("windows")) { 8 | if (System.getProperty("os.arch").indexOf("64") > -1) { 9 | new NativeLibraryLoader().loadLibrary("javasysmon64.dll"); 10 | } else { 11 | new NativeLibraryLoader().loadLibrary("javasysmon.dll"); 12 | } 13 | monitor = new WindowsMonitor(); 14 | } 15 | } 16 | 17 | public WindowsMonitor() { 18 | JavaSysMon.addSupportedConfig("Windows (x86)"); 19 | if (monitor != null) { 20 | JavaSysMon.setMonitor(monitor); 21 | } 22 | } 23 | 24 | public String osName() { 25 | return System.getProperty("os.name"); 26 | } 27 | 28 | public native int numCpus(); 29 | public native int currentPid(); 30 | public native long cpuFrequencyInHz(); 31 | public native long uptimeInSeconds(); 32 | public native CpuTimes cpuTimes(); 33 | public native MemoryStats physical(); 34 | public native MemoryStats swap(); 35 | public native ProcessInfo[] processTable(); 36 | public native void killProcess(int pid); 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/jezhumble/javasysmon/package.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |

JavaSysMon is designed to provide an OS-independent way to get live system information, 6 | such as CPU and memory usage, and manage processes, distributed as a single jar file. 7 | It is written in C and Java. However the native binaries are hidden away inside the jar, 8 | so you never need to worry about them.

9 | 10 |

The main API is to be found in {@link com.jezhumble.javasysmon.JavaSysMon}, so start there.

11 | 12 |

JavaSysMon is licensed according to the terms of the NetBSD (2-line BSD) license.

13 | 14 |

Related Documentation

15 | 16 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/test/java/com/jezhumble/javasysmon/CpuTimesTest.java: -------------------------------------------------------------------------------- 1 | package com.jezhumble.javasysmon; 2 | 3 | import junit.framework.Assert; 4 | import junit.framework.TestCase; 5 | 6 | public class CpuTimesTest extends TestCase { 7 | 8 | public void testShouldReturn1ForCpuFullyUsed() { 9 | CpuTimes cpuTimesPrev = new CpuTimes(100, 100, 0); 10 | CpuTimes cpuTimesNext = new CpuTimes(200, 200, 0); 11 | final float cpuUsage = cpuTimesNext.getCpuUsage(cpuTimesPrev); 12 | assertEqual(1f, cpuUsage); 13 | } 14 | 15 | public void testShouldReturn1IfNoChange() { 16 | CpuTimes cpuTimesPrev = new CpuTimes(100, 100, 0); 17 | CpuTimes cpuTimesNext = new CpuTimes(100, 100, 0); 18 | final float cpuUsage = cpuTimesNext.getCpuUsage(cpuTimesPrev); 19 | assertEqual(1f, cpuUsage); 20 | } 21 | 22 | public void testShouldReturn0IfIdle() { 23 | CpuTimes cpuTimesPrev = new CpuTimes(100, 100, 0); 24 | CpuTimes cpuTimesNext = new CpuTimes(100, 100, 100); 25 | final float cpuUsage = cpuTimesNext.getCpuUsage(cpuTimesPrev); 26 | assertEqual(0f, cpuUsage); 27 | } 28 | 29 | public void testShouldReturnHalfForCpuHalfUsed() { 30 | CpuTimes cpuTimesPrev = new CpuTimes(100, 100, 0); 31 | CpuTimes cpuTimesNext = new CpuTimes(200, 200, 200); 32 | final float cpuUsage = cpuTimesNext.getCpuUsage(cpuTimesPrev); 33 | assertEqual(0.5f, cpuUsage); 34 | } 35 | 36 | private void assertEqual(float expected, float actual) { 37 | Assert.assertTrue("Expected: " + expected + " got: " + actual, Float.compare(expected, actual) == 0); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/test/java/com/jezhumble/javasysmon/LinuxMonitorTest.java: -------------------------------------------------------------------------------- 1 | package com.jezhumble.javasysmon; 2 | 3 | import junit.framework.Assert; 4 | import junit.framework.TestCase; 5 | 6 | public class LinuxMonitorTest extends TestCase { 7 | 8 | public void testShouldRetrieveTotalMemory() { 9 | LinuxMonitor monitor = new LinuxMonitor(new StubFileUtils()); 10 | final long totalMemory = monitor.physical().getTotalBytes(); 11 | Assert.assertEquals((long)368640, totalMemory/1024); 12 | } 13 | 14 | public void testShouldRetrieveFreeMemory() { 15 | LinuxMonitor monitor = new LinuxMonitor(new StubFileUtils()); 16 | final long freeMemory = monitor.physical().getFreeBytes(); 17 | Assert.assertEquals((long)195608, freeMemory/1024); 18 | } 19 | 20 | public void testShouldRetrieveTotalSwap() { 21 | LinuxMonitor monitor = new LinuxMonitor(new StubFileUtils()); 22 | final long totalSwap = monitor.swap().getTotalBytes(); 23 | Assert.assertEquals((long)262144, totalSwap/1024); 24 | } 25 | 26 | public void testShouldRetrieveFreeSwap() { 27 | LinuxMonitor monitor = new LinuxMonitor(new StubFileUtils()); 28 | final long freeSwap = monitor.swap().getFreeBytes(); 29 | Assert.assertEquals((long)260123, freeSwap/1024); 30 | } 31 | 32 | public void testShouldCalculateNumCpus() { 33 | LinuxMonitor monitor = new LinuxMonitor(new StubFileUtils()); 34 | final int numCpus = monitor.numCpus(); 35 | Assert.assertEquals(2, numCpus); 36 | } 37 | 38 | public void testShouldCalculateCpuFrequency() { 39 | LinuxMonitor monitor = new LinuxMonitor(new StubFileUtils()); 40 | final long cpuFrequency = monitor.cpuFrequencyInHz(); 41 | Assert.assertEquals(2400000000l, cpuFrequency); 42 | } 43 | 44 | public void testShouldReturnUptime() { 45 | LinuxMonitor monitor = new LinuxMonitor(new StubFileUtils()); 46 | final long uptime = monitor.uptimeInSeconds(); 47 | Assert.assertEquals(22550744l, uptime); 48 | } 49 | 50 | public void testShouldReturnPid() { 51 | LinuxMonitor monitor = new LinuxMonitor(new StubFileUtils()); 52 | final int pid = monitor.currentPid(); 53 | Assert.assertEquals(31912, pid); 54 | } 55 | 56 | public void testShouldReturnTheProcessTable() { 57 | LinuxMonitor linuxMonitor = new LinuxMonitor(); 58 | Assert.assertNotNull(linuxMonitor.processTable()); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/test/java/com/jezhumble/javasysmon/LinuxProcessInfoParserTest.java: -------------------------------------------------------------------------------- 1 | package com.jezhumble.javasysmon; 2 | 3 | import junit.framework.TestCase; 4 | 5 | import java.io.IOException; 6 | import java.util.HashMap; 7 | 8 | public class LinuxProcessInfoParserTest extends TestCase { 9 | 10 | private static final int USER_HZ = 100; 11 | private static final String COMMAND_LINE = "cl"; 12 | private static final HashMap UIDS = new HashMap(); 13 | private static final int EXPECTED_PID = 25883; 14 | private static final int EXPECTED_PPID = 25097; 15 | 16 | static { 17 | UIDS.put("1000", "tbombadil"); 18 | } 19 | 20 | private FileUtils fileUtils; 21 | 22 | public void setUp() throws Exception { 23 | fileUtils = new FileUtils(); 24 | } 25 | 26 | public void testExpectedCase() throws IOException { 27 | LinuxProcessInfoParser parser = new LinuxProcessInfoParser(getTestFileContents("test_stat_expected"), 28 | getTestFileContents("test_status"), 29 | "cl", UIDS, USER_HZ); 30 | ProcessInfo processInfo = parser.parse(); 31 | assertEquals(EXPECTED_PID, processInfo.getPid()); 32 | assertEquals(EXPECTED_PPID, processInfo.getParentPid()); 33 | assertEquals("test_command", processInfo.getName()); 34 | assertEquals(COMMAND_LINE, processInfo.getCommand()); 35 | assertEquals("tbombadil", processInfo.getOwner()); 36 | assertEquals(1519616L, processInfo.getResidentBytes()); 37 | assertEquals(0L, processInfo.getSystemMillis()); 38 | assertEquals(4554752L, processInfo.getTotalBytes()); 39 | assertEquals(0L, processInfo.getUserMillis()); 40 | } 41 | 42 | public void testCommandWithSpaces() throws IOException { 43 | LinuxProcessInfoParser parser = new LinuxProcessInfoParser(getTestFileContents("test_stat_spaces"), 44 | getTestFileContents("test_status"), 45 | "cl", UIDS, USER_HZ); 46 | ProcessInfo processInfo = parser.parse(); 47 | assertEquals(EXPECTED_PID, processInfo.getPid()); 48 | assertEquals(EXPECTED_PPID, processInfo.getParentPid()); 49 | } 50 | 51 | public void testCommandWithClosingParen() throws IOException { 52 | LinuxProcessInfoParser parser = new LinuxProcessInfoParser(getTestFileContents("test_stat_paren"), 53 | getTestFileContents("test_status"), 54 | "cl", UIDS, USER_HZ); 55 | ProcessInfo processInfo = parser.parse(); 56 | assertEquals(EXPECTED_PID, processInfo.getPid()); 57 | assertEquals(EXPECTED_PPID, processInfo.getParentPid()); 58 | } 59 | 60 | public void testCommandWithClosingParenThenNumbers() throws IOException { 61 | LinuxProcessInfoParser parser = new LinuxProcessInfoParser(getTestFileContents("test_stat_numbers"), 62 | getTestFileContents("test_status"), 63 | "cl", UIDS, USER_HZ); 64 | ProcessInfo processInfo = parser.parse(); 65 | assertEquals(EXPECTED_PID, processInfo.getPid()); 66 | assertEquals(EXPECTED_PPID, processInfo.getParentPid()); 67 | } 68 | 69 | public void testInvalidStatNumbers() throws IOException { 70 | invalidStatTest("test_stat_invalid_numbers", "Unable to parse stat"); 71 | } 72 | 73 | public void testInvalidStatNoParens() throws IOException { 74 | invalidStatTest("test_stat_no_parens", "does not include expected parens around process name"); 75 | } 76 | 77 | public void testInvalidStatTooFewFields() throws IOException { 78 | invalidStatTest("test_stat_few_fields", "contains fewer elements than expected"); 79 | } 80 | 81 | private void invalidStatTest(String statFile, String errorSnippet) throws IOException 82 | { 83 | LinuxProcessInfoParser parser = new LinuxProcessInfoParser(getTestFileContents(statFile), 84 | getTestFileContents("test_status"), 85 | "cl", UIDS, USER_HZ); 86 | try 87 | { 88 | parser.parse(); 89 | fail("Should not be able to parse invalid stat"); 90 | } 91 | catch (ParseException e) 92 | { 93 | assertTrue("Message '" + e.getMessage() + "' does not contain '" + errorSnippet + "'", 94 | e.getMessage().contains(errorSnippet)); 95 | } 96 | } 97 | 98 | private String getTestFileContents(String filename) throws IOException 99 | { 100 | return fileUtils.slurpFromInputStream(ClassLoader.getSystemClassLoader().getResourceAsStream(filename)); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/test/java/com/jezhumble/javasysmon/NativeLibraryLoaderTest.java: -------------------------------------------------------------------------------- 1 | package com.jezhumble.javasysmon; 2 | 3 | import junit.framework.TestCase; 4 | 5 | import java.io.File; 6 | import java.io.IOException; 7 | import java.util.Properties; 8 | 9 | public class NativeLibraryLoaderTest extends TestCase { 10 | private Properties initProperties; 11 | 12 | public void setUp() throws Exception { 13 | initProperties = System.getProperties(); 14 | } 15 | 16 | public void tearDown() throws Exception { 17 | System.clearProperty(NativeLibraryLoader.JAVA_SYS_MON_TEMP_DIR); 18 | System.setProperties(initProperties); 19 | } 20 | 21 | public void testShouldCreateTempFileUnderSysmonTempDirIfPropertyIsSet() throws IOException { 22 | File currentDir = new File("."); 23 | System.setProperty(NativeLibraryLoader.JAVA_SYS_MON_TEMP_DIR, currentDir.getAbsolutePath()); 24 | NativeLibraryLoader loader = new NativeLibraryLoader(); 25 | File tempFile = loader.getTempFile("javasysmon64.dll"); 26 | assertEquals(currentDir.getAbsolutePath(), tempFile.getParentFile().getAbsolutePath()); 27 | } 28 | 29 | public void testShouldCreateTempFileUnderDefaultTempDirIfPropertyIsNotSet() throws IOException { 30 | NativeLibraryLoader loader = new NativeLibraryLoader(); 31 | File tempFile = loader.getTempFile("javasysmon64.dll"); 32 | assertEquals(new File(System.getProperty("java.io.tmpdir")), tempFile.getParentFile()); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/test/java/com/jezhumble/javasysmon/OsProcessTest.java: -------------------------------------------------------------------------------- 1 | package com.jezhumble.javasysmon; 2 | 3 | import junit.framework.Assert; 4 | import junit.framework.TestCase; 5 | 6 | public class OsProcessTest extends TestCase { 7 | private static ProcessInfo[] info = { 8 | new ProcessInfo(0, 0, "", "init", "", 0, 0, 0, 0), 9 | new ProcessInfo(1, 0, "", "login", "", 0, 0, 0, 0), 10 | new ProcessInfo(2, 0, "", "daemon", "", 0, 0, 0, 0), 11 | new ProcessInfo(3, 1, "", "bash", "", 0, 0, 0, 0), 12 | new ProcessInfo(4, 5, "", "orphan", "", 0, 0, 0, 0) 13 | }; 14 | 15 | public void testShouldCreateProcessTree() { 16 | OsProcess virtualNode = OsProcess.createTree(info); 17 | Assert.assertEquals(virtualNode.children().size(), 2); 18 | } 19 | 20 | public void testShouldFindDescendants() { 21 | OsProcess virtualNode = OsProcess.createTree(info); 22 | Assert.assertEquals("bash", virtualNode.find(3).processInfo().getName()); 23 | Assert.assertEquals(virtualNode.find(50), null); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/test/java/com/jezhumble/javasysmon/ProcessTreeFunctionalTest.java: -------------------------------------------------------------------------------- 1 | package com.jezhumble.javasysmon; 2 | 3 | import junit.framework.Assert; 4 | import junit.framework.TestCase; 5 | 6 | public class ProcessTreeFunctionalTest extends TestCase { 7 | public void testShouldKillChildProcesses() { 8 | try { 9 | JavaSysMon monitor = new JavaSysMon(); 10 | Assert.assertEquals(0, monitor.processTree().find(monitor.currentPid()).children().size()); 11 | Runtime.getRuntime().exec("ant sleep"); 12 | Runtime.getRuntime().exec("sleep 50"); 13 | Assert.assertEquals(2, monitor.processTree().find(monitor.currentPid()).children().size()); 14 | monitor.infanticide(); 15 | Thread.sleep(500); 16 | Assert.assertEquals(0, monitor.processTree().find(monitor.currentPid()).children().size()); 17 | } catch (Exception e) { 18 | Assert.fail("Exception: " + e.getMessage()); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/test/java/com/jezhumble/javasysmon/StubFileUtils.java: -------------------------------------------------------------------------------- 1 | package com.jezhumble.javasysmon; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | 6 | public class StubFileUtils extends FileUtils { 7 | boolean alreadyGotStat = false; 8 | 9 | public String slurp(String fileName) throws IOException { 10 | InputStream testFile = null; 11 | if (fileName.equals("/proc/uptime")) { 12 | testFile = getTestFile("test_uptime"); 13 | } 14 | if (fileName.equals("/proc/self/stat")) { 15 | testFile = getTestFile("test_self_stat"); 16 | } 17 | if (fileName.equals("/proc/meminfo")) { 18 | testFile = getTestFile("test_meminfo"); 19 | } 20 | if (fileName.equals("/proc/cpuinfo")) { 21 | testFile = getTestFile("test_cpuinfo"); 22 | } 23 | if (fileName.equals("/proc/stat")) { 24 | testFile = alreadyGotStat ? getTestFile("test_stat_1") : getTestFile("test_stat_0"); 25 | alreadyGotStat = true; 26 | } 27 | if (testFile != null) { 28 | return slurpFromInputStream(testFile); 29 | } 30 | return null; 31 | } 32 | 33 | private InputStream getTestFile(String filename) { 34 | return ClassLoader.getSystemClassLoader().getResourceAsStream(filename); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/test/java/com/jezhumble/javasysmon/UnixPasswdParserTest.java: -------------------------------------------------------------------------------- 1 | package com.jezhumble.javasysmon; 2 | 3 | import junit.framework.Assert; 4 | import junit.framework.TestCase; 5 | 6 | import java.io.BufferedReader; 7 | import java.io.StringReader; 8 | import java.util.HashMap; 9 | 10 | public class UnixPasswdParserTest extends TestCase { 11 | 12 | public void testShouldHandleEmptyLineInPasswdFile() { 13 | String emptyLine = "+::::::\n"; 14 | BufferedReader reader = new BufferedReader(new StringReader(emptyLine)); 15 | UnixPasswdParser unixPasswdParser = new UnixPasswdParser(); 16 | final HashMap passwd = unixPasswdParser.parse(reader); 17 | Assert.assertEquals(0, passwd.size()); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/test/resources/test_cpuinfo: -------------------------------------------------------------------------------- 1 | processor : 0 2 | vendor_id : GenuineIntel 3 | cpu family : 6 4 | model : 15 5 | model name : Intel(R) Xeon(R) CPU 3060 @ 2.40GHz 6 | stepping : 6 7 | cpu MHz : 598.516 8 | cache size : 4096 KB 9 | physical id : 0 10 | siblings : 2 11 | core id : 0 12 | cpu cores : 2 13 | fpu : yes 14 | fpu_exception : yes 15 | cpuid level : 10 16 | wp : yes 17 | flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm syscall nx lm constant_tsc pni monitor ds_cpl vmx est tm2 cx16 xtpr lahf_lm 18 | bogomips : 4789.99 19 | clflush size : 64 20 | cache_alignment : 64 21 | address sizes : 36 bits physical, 48 bits virtual 22 | power management: 23 | 24 | processor : 1 25 | vendor_id : GenuineIntel 26 | cpu family : 6 27 | model : 15 28 | model name : Intel(R) Xeon(R) CPU 3060 @ 2.40GHz 29 | stepping : 6 30 | cpu MHz : 598.516 31 | cache size : 4096 KB 32 | physical id : 0 33 | siblings : 2 34 | core id : 1 35 | cpu cores : 2 36 | fpu : yes 37 | fpu_exception : yes 38 | cpuid level : 10 39 | wp : yes 40 | flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm syscall nx lm constant_tsc pni monitor ds_cpl vmx est tm2 cx16 xtpr lahf_lm 41 | bogomips : 4787.37 42 | clflush size : 64 43 | cache_alignment : 64 44 | address sizes : 36 bits physical, 48 bits virtual 45 | power management: 46 | 47 | -------------------------------------------------------------------------------- /src/test/resources/test_meminfo: -------------------------------------------------------------------------------- 1 | MemTotal: 368640 kB 2 | MemFree: 195608 kB 3 | Buffers: 0 kB 4 | Cached: 0 kB 5 | SwapCached: 0 kB 6 | Active: 0 kB 7 | Inactive: 0 kB 8 | HighTotal: 0 kB 9 | HighFree: 0 kB 10 | LowTotal: 368640 kB 11 | LowFree: 195608 kB 12 | SwapTotal: 262144 kB 13 | SwapFree: 260123 kB 14 | Dirty: 0 kB 15 | Writeback: 0 kB 16 | AnonPages: 0 kB 17 | Mapped: 173032 kB 18 | Slab: 0 kB 19 | PageTables: 0 kB 20 | NFS_Unstable: 0 kB 21 | Bounce: 0 kB 22 | CommitLimit: 0 kB 23 | Committed_AS: 310748 kB 24 | VmallocTotal: 0 kB 25 | VmallocUsed: 0 kB 26 | VmallocChunk: 0 kB 27 | HugePages_Total: 0 28 | HugePages_Free: 0 29 | HugePages_Rsvd: 0 30 | Hugepagesize: 2048 kB -------------------------------------------------------------------------------- /src/test/resources/test_self_stat: -------------------------------------------------------------------------------- 1 | 31912 (apache2) S 1 31912 31912 0 -1 4202816 5664 3813965 0 1008 11773 3664 254790 11050 15 0 1 0 2144745137 24621056 2217 18446744073709551615 134512640 134850660 3220954656 18446744073709551615 3085590072 0 0 4096 134235883 0 0 0 17 0 0 0 9 0 0 0 0 0 0 0 31912 613 613.0 613.0 -------------------------------------------------------------------------------- /src/test/resources/test_stat_0: -------------------------------------------------------------------------------- 1 | cpu 5058715 1290 1882350 4359738256 11075780 0 0 0 2 | cpu0 2545332 13 960684 2177709879 5695143 0 0 0 3 | cpu1 2513383 1276 921665 2182028376 5380637 0 0 0 4 | intr 0 5 | swap 0 0 6 | ctxt 64968726131 7 | btime 1238881965 8 | processes 305070333 9 | procs_running 2 10 | procs_blocked 0 -------------------------------------------------------------------------------- /src/test/resources/test_stat_1: -------------------------------------------------------------------------------- 1 | cpu 5058756 1290 1882355 4359776821 11075803 0 0 0 2 | cpu0 2545368 13 960685 2177729226 5695163 0 0 0 3 | cpu1 2513387 1276 921669 2182047594 5380639 0 0 0 4 | intr 0 5 | swap 0 0 6 | ctxt 64969303655 7 | btime 1238881965 8 | processes 305072127 9 | procs_running 2 10 | procs_blocked 0 -------------------------------------------------------------------------------- /src/test/resources/test_stat_expected: -------------------------------------------------------------------------------- 1 | 25883 (test_command) S 25097 25883 25097 34817 29940 4194304 508 0 0 0 0 0 0 0 20 0 1 0 248947866 4554752 371 4294967295 134512640 135220192 3218176816 3218175880 3078972464 0 65536 4 65538 3222524285 0 0 17 1 0 0 0 0 0 -------------------------------------------------------------------------------- /src/test/resources/test_stat_few_fields: -------------------------------------------------------------------------------- 1 | 25883 (test_command) S 25097 25883 25097 34817 29940 4194304 508 0 0 -------------------------------------------------------------------------------- /src/test/resources/test_stat_invalid_numbers: -------------------------------------------------------------------------------- 1 | 25883 (invalid) S bad numbers are all around us 508 0 0 0 0 0 0 0 20 0 1 0 248947866 4554752 371 4294967295 134512640 135220192 3218176816 3218175880 3078972464 0 65536 4 65538 3222524285 0 0 17 1 0 0 0 0 0 -------------------------------------------------------------------------------- /src/test/resources/test_stat_no_parens: -------------------------------------------------------------------------------- 1 | 25883 test_command S 25097 25883 25097 34817 29940 4194304 508 0 0 0 0 0 0 0 20 0 1 0 248947866 4554752 371 4294967295 134512640 135220192 3218176816 3218175880 3078972464 0 65536 4 65538 3222524285 0 0 17 1 0 0 0 0 0 -------------------------------------------------------------------------------- /src/test/resources/test_stat_numbers: -------------------------------------------------------------------------------- 1 | 25883 () 1 2 3 4 5 6 7) S 25097 25883 25097 34817 29940 4194304 508 0 0 0 0 0 0 0 20 0 1 0 248947866 4554752 371 4294967295 134512640 135220192 3218176816 3218175880 3078972464 0 65536 4 65538 3222524285 0 0 17 1 0 0 0 0 0 -------------------------------------------------------------------------------- /src/test/resources/test_stat_paren: -------------------------------------------------------------------------------- 1 | 25883 (hello :)) S 25097 25883 25097 34817 29940 4194304 508 0 0 0 0 0 0 0 20 0 1 0 248947866 4554752 371 4294967295 134512640 135220192 3218176816 3218175880 3078972464 0 65536 4 65538 3222524285 0 0 17 1 0 0 0 0 0 -------------------------------------------------------------------------------- /src/test/resources/test_stat_spaces: -------------------------------------------------------------------------------- 1 | 25883 (test w/ spaces) S 25097 25883 25097 34817 29940 4194304 508 0 0 0 0 0 0 0 20 0 1 0 248947866 4554752 371 4294967295 134512640 135220192 3218176816 3218175880 3078972464 0 65536 4 65538 3222524285 0 0 17 1 0 0 0 0 0 -------------------------------------------------------------------------------- /src/test/resources/test_status: -------------------------------------------------------------------------------- 1 | Name: test_command 2 | State: S (sleeping) 3 | Tgid: 25883 4 | Pid: 25883 5 | PPid: 25097 6 | TracerPid: 0 7 | Uid: 1000 1000 1000 1000 8 | Gid: 1000 1000 1000 1000 9 | FDSize: 256 10 | Groups: 4 20 24 46 108 123 124 1000 11 | VmPeak: 4448 kB 12 | VmSize: 4448 kB 13 | VmLck: 0 kB 14 | VmHWM: 1484 kB 15 | VmRSS: 1484 kB 16 | VmData: 640 kB 17 | VmStk: 88 kB 18 | VmExe: 692 kB 19 | VmLib: 1700 kB 20 | VmPTE: 20 kB 21 | Threads: 1 22 | SigQ: 0/16382 23 | SigPnd: 0000000000000000 24 | ShdPnd: 0000000000000000 25 | SigBlk: 0000000000010000 26 | SigIgn: 0000000000000004 27 | SigCgt: 0000000000010002 28 | CapInh: 0000000000000000 29 | CapPrm: 0000000000000000 30 | CapEff: 0000000000000000 31 | CapBnd: ffffffffffffffff 32 | Cpus_allowed: 00000000,0000000f 33 | Cpus_allowed_list: 0-3 34 | Mems_allowed: 1 35 | Mems_allowed_list: 0 36 | voluntary_ctxt_switches: 1 37 | nonvoluntary_ctxt_switches: 2 38 | -------------------------------------------------------------------------------- /src/test/resources/test_uptime: -------------------------------------------------------------------------------- 1 | 22550744.30 17792617.89 2 | --------------------------------------------------------------------------------