changeset 2575:902e2e96f4e8

A preliminary port of Thermostat to macos. It is quite usable except for statistics that query the OS (like memory and IO usage over time). The main unfinished business: - native code for memory and IO usage - JUnit - GUI often hangs on exit. - Mac .app bundle build Reviewed-by: neugens Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2017-January/022086.html
author Simon Tooke <stooke@redhat.com>
date Mon, 30 Jan 2017 13:06:07 -0500
parents cd5b08c32052
children 15a168c5a68d
files agent/command/src/main/java/com/redhat/thermostat/agent/command/internal/ProcessUserInfoBuilder.java common/portability/Makefile common/portability/pom.xml common/portability/src/main/java/com/redhat/thermostat/common/portability/PortableHostImpl.java common/portability/src/main/java/com/redhat/thermostat/common/portability/PortableProcessImpl.java common/portability/src/main/java/com/redhat/thermostat/common/portability/ProcessChecker.java common/portability/src/main/java/com/redhat/thermostat/common/portability/ProcessUserInfo.java common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/macos/MacOSHelperImpl.java common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/macos/MacOSHostImpl.java common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/macos/MacOSMemoryStat.java common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/macos/MacOSProcessImpl.java common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/macos/MacOSUserInfoBuilderImpl.java common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/macos/MacOSVmIoStat.java common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/windows/WindowsHelperImpl.java common/portability/src/main/native/MacOSHelperImpl.c common/portability/src/main/native/WindowsHelperImpl.c distribution/config/thermostatrc distribution/pom.xml distribution/scripts/thermostat-command-channel distribution/scripts/thermostat-common keyring/pom.xml laf-utils/pom.xml pom.xml process-handler/src/main/java/com/redhat/thermostat/service/internal/unix/UnixProcessUtilities.java
diffstat 24 files changed, 1148 insertions(+), 122 deletions(-) [+]
line wrap: on
line diff
--- a/agent/command/src/main/java/com/redhat/thermostat/agent/command/internal/ProcessUserInfoBuilder.java	Mon Jan 30 10:30:19 2017 -0500
+++ b/agent/command/src/main/java/com/redhat/thermostat/agent/command/internal/ProcessUserInfoBuilder.java	Mon Jan 30 13:06:07 2017 -0500
@@ -36,6 +36,8 @@
 
 package com.redhat.thermostat.agent.command.internal;
 
+import com.redhat.thermostat.shared.config.OS;
+
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileReader;
@@ -46,27 +48,27 @@
  * Replace when this information is available from an API.
  */
 class ProcessUserInfoBuilder {
-    
+
     private static final String PROC_STATUS_SELF_PATH = "/proc/self/status";
     private static final String PROC_STATUS_UID = "Uid:";
     private final FileReaderCreator readerCreator;
-    
+
     ProcessUserInfoBuilder() {
         this(new FileReaderCreator());
     }
-    
+
     ProcessUserInfoBuilder(FileReaderCreator readerCreator) {
         this.readerCreator = readerCreator;
     }
-    
+
     private long getUid() throws IOException {
         FileReader reader = readerCreator.create(PROC_STATUS_SELF_PATH);
         long uid = getUidFromProcfs(new BufferedReader(reader));
         return uid;
     }
-    
+
     boolean isPrivilegedUser() throws IOException {
-        return (getUid() == 0);
+        return OS.IS_LINUX ? (getUid() == 0) : false;
     }
 
     /*
--- a/common/portability/Makefile	Mon Jan 30 10:30:19 2017 -0500
+++ b/common/portability/Makefile	Mon Jan 30 13:06:07 2017 -0500
@@ -7,17 +7,25 @@
 CLASSPATH  = target/classes/
 TARGET_DIR = target
 
-ifeq ($(OS),Windows_NT)
+ifeq ($(JNI_PLATFORM),win32)
     JNI_PLATFORM = win32
     SO_PREFIX  =
     SO_SUFFIX  = .dll
 else
+ifeq ($(JNI_PLATFORM),darwin)
+    JNI_PLATFORM = darwin
+    SO_PREFIX  = lib
+    SO_SUFFIX  = .dylib
+    MYCFLAGS   +=
+    MYLDFLAGS  +=
+else
     JNI_PLATFORM = linux
     SO_PREFIX  = lib
     SO_SUFFIX  = .so
     MYCFLAGS   += -fPIC
     MYLDFLAGS  += -fPIC
 endif
+endif
 
 INCLUDE    = -I $(TARGET_DIR) -I "$(JAVA_HOME)/include/" -I "$(JAVA_HOME)/include/$(JNI_PLATFORM)"
 
@@ -31,31 +39,48 @@
 USERNAME_OBJECTS    = $(USERNAME_TARGET:.c=.o)
 USERNAME_EXECUTABLE = $(SO_PREFIX)UserNameUtilWrapper$(SO_SUFFIX)
 
-WINHELPER_SOURCES    = src/main/native/WindowsHelperImpl.c
-WINHELPER_TARGET     = $(TARGET_DIR)/WindowsHelperImpl.c
-WINHELPER_OBJECTS    = $(WINHELPER_TARGET:.c=.o)
-WINHELPER_EXECUTABLE = $(SO_PREFIX)WindowsHelperWrapper$(SO_SUFFIX)
+ifeq ($(JNI_PLATFORM),win32)
+HELPER_SOURCES    = src/main/native/WindowsHelperImpl.c
+HELPER_TARGET     = $(TARGET_DIR)/WindowsHelperImpl.c
+HELPER_OBJECTS    = $(HELPER_TARGET:.c=.o)
+HELPER_EXECUTABLE = $(SO_PREFIX)WindowsHelperWrapper$(SO_SUFFIX)
+endif
+
+ifeq ($(JNI_PLATFORM),darwin)
+HELPER_SOURCES    = src/main/native/MacOSHelperImpl.c
+HELPER_TARGET     = $(TARGET_DIR)/MacOSHelperImpl.c
+HELPER_OBJECTS    = $(HELPER_TARGET:.c=.o)
+HELPER_EXECUTABLE = $(SO_PREFIX)MacOSHelperWrapper$(SO_SUFFIX)
+endif
 
 EXECUTABLES          = $(HOSTNAME_EXECUTABLE) $(USERNAME_EXECUTABLE)
 
 .PHONY:UserNameUtilImpl
 JNI_LIST = com.redhat.thermostat.common.portability.HostName com.redhat.thermostat.common.portability.internal.linux.UserNameUtilImpl
 
-ifeq ($(OS),Windows_NT)
-    EXECUTABLES  += $(WINHELPER_EXECUTABLE)
+ifeq ($(JNI_PLATFORM),win32)
+    EXECUTABLES  += $(HELPER_EXECUTABLE)
     JNI_LIST     +=  com.redhat.thermostat.common.portability.internal.windows.WindowsHelperImpl
+    HELPER_LIBS  += -l psapi
+endif
+
+ifeq ($(JNI_PLATFORM),darwin)
+    EXECUTABLES  += $(HELPER_EXECUTABLE)
+    JNI_LIST     +=  com.redhat.thermostat.common.portability.internal.macos.MacOSHelperImpl
 endif
 
 $(JNI_LIST):
 	$(JAVAH) -force -classpath $(CLASSPATH) -d $(TARGET_DIR) $(JNI_LIST)
 
-all: $(JNI_LIST) init $(HOSTNAME_SOURCES) $(USERNAME_SOURCES) $(WINHELPER_SOURCES) $(EXECUTABLES)
+all: $(JNI_LIST) init $(HOSTNAME_SOURCES) $(USERNAME_SOURCES) $(HELPER_SOURCES) $(EXECUTABLES)
 
 .PHONY:
 init:
 	$(COPY) $(HOSTNAME_SOURCES) $(HOSTNAME_TARGET)
 	$(COPY) $(USERNAME_SOURCES) $(USERNAME_TARGET)
-	$(COPY) $(WINHELPER_SOURCES) $(WINHELPER_TARGET)
+ifneq ($(strip $(HELPER_SOURCES)),)
+	$(COPY) $(HELPER_SOURCES) $(HELPER_TARGET)
+endif
 
 $(HOSTNAME_EXECUTABLE): $(HOSTNAME_OBJECTS)
 	$(CC) $(MYLDFLAGS) $(LDFLAGS) $(HOSTNAME_OBJECTS) $(PLATFORM_LIBS) -o $(TARGET_DIR)/$@
@@ -63,8 +88,8 @@
 $(USERNAME_EXECUTABLE): $(USERNAME_OBJECTS)
 	$(CC) $(MYLDFLAGS) $(LDFLAGS) $(USERNAME_OBJECTS) $(PLATFORM_LIBS) -o $(TARGET_DIR)/$@
 
-$(WINHELPER_EXECUTABLE): $(WINHELPER_OBJECTS)
-	$(CC) $(MYLDFLAGS) $(LDFLAGS) $(WINHELPER_OBJECTS) $(PLATFORM_LIBS) -l psapi -o $(TARGET_DIR)/$@
+$(HELPER_EXECUTABLE): $(HELPER_OBJECTS)
+	$(CC) $(MYLDFLAGS) $(LDFLAGS) $(HELPER_OBJECTS) $(PLATFORM_LIBS) $(HELPER_LIBS) -o $(TARGET_DIR)/$@
 
 .c.o:
 	$(CC) $(MYCFLAGS) $(CFLAGS) $(INCLUDE) $< -o $@
@@ -75,12 +100,12 @@
 clean-lib:
 	rm -f $(TARGET_DIR)/$(HOSTNAME_EXECUTABLE)
 	rm -f $(TARGET_DIR)/$(USERNAME_EXECUTABLE)
-	rm -f $(TARGET_DIR)/$(WINHELPER_EXECUTABLE)
+	rm -f $(TARGET_DIR)/$(HELPER_EXECUTABLE)
 
 clean-obj:
 	rm -f $(HOSTNAME_OBJECTS) $(HOSTNAME_TARGET)
 	rm -f $(USERNAME_OBJECTS) $(USERNAME_TARGET)
-	rm -f $(WINHELPER_OBJECTS) $(WINHELPER_TARGET)
+	rm -f $(HELPER_OBJECTS) $(HELPER_TARGET)
 
 clean: clean-obj clean-lib
 
--- a/common/portability/pom.xml	Mon Jan 30 10:30:19 2017 -0500
+++ b/common/portability/pom.xml	Mon Jan 30 13:06:07 2017 -0500
@@ -61,7 +61,7 @@
         <profile>
             <id>linux</id>
             <activation>
-                <os><family>Unix</family></os>
+                <os><name>linux</name></os>
             </activation>
             <properties>
                 <platform.libs/>
@@ -69,6 +69,17 @@
         </profile>
 
         <profile>
+            <id>macos</id>
+            <activation>
+                <os><family>mac</family></os>
+            </activation>
+            <properties>
+                <platform.libs/>
+                <sharedlib.suffix>.dylib</sharedlib.suffix>
+            </properties>
+        </profile>
+
+        <profile>
             <id>windows</id>
             <activation>
                 <os><family>Windows</family></os>
@@ -140,7 +151,8 @@
                             com.redhat.thermostat.common.portability.internal,
                             com.redhat.thermostat.common.portability.internal.linux,
                             com.redhat.thermostat.common.portability.internal.linux.vmio,
-                            com.redhat.thermostat.common.portability.internal.windows
+                            com.redhat.thermostat.common.portability.internal.windows,
+                            com.redhat.thermostat.common.portability.internal.macos
                         </Private-Package>
                         <!-- Do not autogenerate uses clauses in Manifests -->
                         <_nouses>true</_nouses>
--- a/common/portability/src/main/java/com/redhat/thermostat/common/portability/PortableHostImpl.java	Mon Jan 30 10:30:19 2017 -0500
+++ b/common/portability/src/main/java/com/redhat/thermostat/common/portability/PortableHostImpl.java	Mon Jan 30 13:06:07 2017 -0500
@@ -37,6 +37,7 @@
 package com.redhat.thermostat.common.portability;
 
 import com.redhat.thermostat.common.portability.internal.linux.LinuxPortableHostImpl;
+import com.redhat.thermostat.common.portability.internal.macos.MacOSHostImpl;
 import com.redhat.thermostat.common.portability.internal.windows.WindowsPortableHostImpl;
 import com.redhat.thermostat.shared.config.OS;
 
@@ -46,7 +47,7 @@
 
     private static PortableHost createInstance() {
         return OS.IS_LINUX ? LinuxPortableHostImpl.createInstance()
-                : OS.IS_WINDOWS ? WindowsPortableHostImpl.createInstance()  : null;
+                : OS.IS_WINDOWS ? WindowsPortableHostImpl.createInstance() : MacOSHostImpl.INSTANCE;
     }
 
     public static PortableHost getInstance() {
--- a/common/portability/src/main/java/com/redhat/thermostat/common/portability/PortableProcessImpl.java	Mon Jan 30 10:30:19 2017 -0500
+++ b/common/portability/src/main/java/com/redhat/thermostat/common/portability/PortableProcessImpl.java	Mon Jan 30 13:06:07 2017 -0500
@@ -37,6 +37,7 @@
 package com.redhat.thermostat.common.portability;
 
 import com.redhat.thermostat.common.portability.internal.linux.LinuxPortableProcessImpl;
+import com.redhat.thermostat.common.portability.internal.macos.MacOSProcessImpl;
 import com.redhat.thermostat.common.portability.internal.windows.WindowsPortableProcessImpl;
 import com.redhat.thermostat.shared.config.OS;
 
@@ -46,7 +47,7 @@
 
     private static PortableProcess createInstance() {
         return OS.IS_LINUX ? LinuxPortableProcessImpl.createInstance()
-            : OS.IS_WINDOWS ? WindowsPortableProcessImpl.createInstance() : null;
+            : OS.IS_WINDOWS ? WindowsPortableProcessImpl.createInstance() : MacOSProcessImpl.INSTANCE;
     }
 
     public static PortableProcess getInstance() {
--- a/common/portability/src/main/java/com/redhat/thermostat/common/portability/ProcessChecker.java	Mon Jan 30 10:30:19 2017 -0500
+++ b/common/portability/src/main/java/com/redhat/thermostat/common/portability/ProcessChecker.java	Mon Jan 30 13:06:07 2017 -0500
@@ -36,13 +36,14 @@
 
 package com.redhat.thermostat.common.portability;
 
+import com.redhat.thermostat.common.portability.internal.macos.MacOSHelperImpl;
 import com.redhat.thermostat.common.portability.internal.windows.WindowsHelperImpl;
 import com.redhat.thermostat.shared.config.OS;
 
 import java.io.File;
 /**
  * Utility for checking whether a process exists or not.
- * 
+ *
  * Implementation note: This is Linux specific.
  *
  */
@@ -51,7 +52,7 @@
     private static final boolean is_linux = OS.IS_LINUX;
 
     public boolean exists(int pid) {
-        return is_linux ? existsLinux(pid) : existsWindows(pid);
+        return OS.IS_LINUX ? existsLinux(pid) : (OS.IS_WINDOWS ? existsWindows(pid) : existsMacOS(pid));
     }
 
     private boolean existsLinux(int pid) {
@@ -59,13 +60,17 @@
         return procFile.exists();
     }
 
+    private boolean existsMacOS(int pid) {
+        return MacOSHelperImpl.INSTANCE.exists(pid);
+    }
+
     private boolean existsWindows(int pid) {
         return WindowsHelperImpl.INSTANCE.exists(pid);
     }
-    
+
     // testing-hook
     File mapToFile(int pid) {
         return new File("/proc/" + pid);
     }
-    
+
 }
--- a/common/portability/src/main/java/com/redhat/thermostat/common/portability/ProcessUserInfo.java	Mon Jan 30 10:30:19 2017 -0500
+++ b/common/portability/src/main/java/com/redhat/thermostat/common/portability/ProcessUserInfo.java	Mon Jan 30 13:06:07 2017 -0500
@@ -36,7 +36,9 @@
 
 package com.redhat.thermostat.common.portability;
 
+import com.redhat.thermostat.common.portability.internal.UnimplementedError;
 import com.redhat.thermostat.common.portability.internal.linux.LinuxProcessUserInfoBuilderImpl;
+import com.redhat.thermostat.common.portability.internal.macos.MacOSUserInfoBuilderImpl;
 import com.redhat.thermostat.common.portability.linux.ProcDataSource;
 import com.redhat.thermostat.common.portability.internal.windows.WindowsUserInfoBuilderImpl;
 import com.redhat.thermostat.shared.config.OS;
@@ -65,10 +67,33 @@
     }
 
     public static ProcessUserInfoBuilder createBuilder(ProcDataSource source, UserNameUtil userNameUtil) {
-        return OS.IS_LINUX ? new LinuxProcessUserInfoBuilderImpl(source, userNameUtil) : new WindowsUserInfoBuilderImpl();
+        final ProcessUserInfoBuilder builder;
+        if (OS.IS_LINUX) {
+            builder = new LinuxProcessUserInfoBuilderImpl(source, userNameUtil);
+        }
+        else if (OS.IS_WINDOWS) {
+            builder = new WindowsUserInfoBuilderImpl();
+        }
+        else if (OS.IS_MACOS) {
+            builder = new MacOSUserInfoBuilderImpl();
+        }
+        else {
+            throw new UnimplementedError("ProcessUserInfo");
+        }
+        return builder;
     }
 
     public static ProcessUserInfoBuilder createBuilder() {
-        return OS.IS_LINUX ? new LinuxProcessUserInfoBuilderImpl() : new WindowsUserInfoBuilderImpl();
+        final ProcessUserInfoBuilder builder;
+        if (OS.IS_LINUX) {
+            builder = new LinuxProcessUserInfoBuilderImpl();
+        }
+        else if (OS.IS_WINDOWS) {
+            builder = new WindowsUserInfoBuilderImpl();
+        }
+        else {
+            builder = new MacOSUserInfoBuilderImpl();
+        }
+        return builder;
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/macos/MacOSHelperImpl.java	Mon Jan 30 13:06:07 2017 -0500
@@ -0,0 +1,210 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.common.portability.internal.macos;
+
+import com.redhat.thermostat.common.utils.LoggingUtils;
+import com.redhat.thermostat.shared.config.NativeLibraryResolver;
+import com.redhat.thermostat.shared.config.OS;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Logger;
+
+/**
+ * Utility class to access Windows native code
+ */
+public class MacOSHelperImpl {
+
+    private static final Logger logger = LoggingUtils.getLogger(MacOSHelperImpl.class);
+
+    private static int pagesize = 0;
+
+    public static MacOSHelperImpl INSTANCE;
+
+    static {
+        if (OS.IS_MACOS) {
+            String lib = NativeLibraryResolver.getAbsoluteLibraryPath("MacOSHelperWrapper");
+            try {
+                System.load(lib);
+                INSTANCE = new MacOSHelperImpl();
+                pagesize = (int)getLongSysctl0("vm.pagesize");
+            } catch (UnsatisfiedLinkError e) {
+                logger.severe("Could not load MacOSHelperWrapper DLL:" + lib);
+                INSTANCE = null;
+                // do not throw here, because you'll get a NoClassDefFound thrown when running other tests that Mock this class
+            }
+        } else {
+            INSTANCE = null;
+        }
+    }
+
+    private MacOSHelperImpl() {
+    }
+    // local host-wide information
+
+    public String getHostName() {
+        return getHostName0();
+    }
+
+    String getOSName() {
+        return System.getProperty("os.name") + " " + System.getProperty("os.version");
+    }
+
+    String getOSVersion() {
+        final String ostype = getStringSysctl0("kern.ostype");
+        final String osrelease = getStringSysctl0("kern.osrelease");
+        return ostype + " (Build " + osrelease + ")";
+    }
+
+    String getCPUModel() {
+        return getStringSysctl0("machdep.cpu.brand_string");
+    }
+
+    public int getCPUCount() {
+        return (int)getLongSysctl0("hw.logicalcpu"); // factors in hyperthreads
+        //return (int)getLongSysctl0("hw.physicalcpu"); (excludes hyperthreading)
+    }
+
+    public long getTotalMemory() {
+        return getLongSysctl0("hw.memsize");
+    }
+
+    long[] getMemoryInfo() {
+        /*
+            public PortableMemoryStat(long timeStamp,
+            long total, long free, long buffers,
+            long cached, long swapTotal, long swapFree, long commitLimit)
+         */
+        final long[] mi = new long[8];
+        getGlobalMemoryStatus0(mi);
+        return mi;
+    }
+
+    public long getClockTicksPerSecond() {
+        // we "know" this is 10E7 (units from  getProcessStat)
+        // but calculate it anyways from current process
+        final long[] info = INSTANCE.getProcessCPUInfo(0);
+        return info[4];
+    }
+
+    // local process-specific information
+
+    public boolean exists(int pid) {
+        return getUid(pid) >= 0;
+    }
+
+    public String getUserName(int pid) {
+        return getUserName0(pid);
+    }
+
+    public int getUid(int pid) {
+        final long uid = getProcessUid0(pid);
+        return (int)uid;
+    }
+
+    Map<String, String> getEnvironment(int pid) {
+        // the environment is returned as a 1D array of alternating env names and values
+        final String[] envArray = getEnvironment0(pid);
+        if (envArray == null || envArray.length == 0) {
+            return Collections.emptyMap();
+        }
+
+        if (envArray.length % 2 != 0) {
+            throw new AssertionError("environment array length not even");
+        }
+
+        final Map<String, String> env = new HashMap<>(envArray.length/2);
+        for (int i = 0; i < envArray.length / 2; i++) {
+            env.put(envArray[i * 2], envArray[i * 2 + 1]);
+        }
+        return env;
+    }
+
+    /**
+     * fetch process counters
+     * @param pid process id
+     * @return long[] working set size, user time, kernel time, (todo: elapsed time), ticks per second
+     */
+    long[] getProcessCPUInfo(int pid) {
+        final long[] info = new long[5];
+        getProcessInfo0(pid, info);
+        return info;
+    }
+
+    long[] getProcessMemInfo(int pid) {
+        final long[] info = new long[5];
+        getProcessInfo0(pid, info);
+        return info;
+    }
+
+    long[] getProcessIOInfo(int pid) {
+        final long info[] = new long[6];
+        getProcessIOInfo0(pid,info);
+        return info;
+    }
+
+    boolean terminateProcess(int pid) {
+        return terminateProcess0(pid,0, -1);
+    }
+
+    boolean terminateProcess(int pid, boolean wait) {
+        return terminateProcess0(pid,0, 0);
+    }
+
+    boolean terminateProcess(int pid, int exitcode, int waitMillis) {
+        return terminateProcess0(pid, exitcode, waitMillis);
+    }
+
+    public static native long getLongSysctl0( String name );
+    public static native String getStringSysctl0( String name );
+
+    private static native String getHostName0();
+    private static native boolean getGlobalMemoryStatus0(long[] info);
+    private static native boolean getPerformanceInfo0(long[] info);
+    private static native long queryPerformanceFrequency0();
+
+    private static native String getUserName0(int pid);
+    private static native long getProcessUid0(int pid);
+    private static native String[] getEnvironment0(int pid);
+    private static native boolean getProcessInfo0(int pid, long[] info);
+    private static native boolean getProcessIOInfo0(int pid, long[] info);
+
+    private static native long getCurrentProcessPid0();
+
+    private static native boolean terminateProcess0(int pid, int exitCode, int waitMillis);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/macos/MacOSHostImpl.java	Mon Jan 30 13:06:07 2017 -0500
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.common.portability.internal.macos;
+
+import com.redhat.thermostat.common.portability.PortableHost;
+import com.redhat.thermostat.common.portability.PortableMemoryStat;
+
+public class MacOSHostImpl implements PortableHost {
+
+    public static final MacOSHostImpl INSTANCE = new MacOSHostImpl();
+    private static final MacOSHelperImpl helper = MacOSHelperImpl.INSTANCE;
+
+    @Override
+    public String getHostName() {
+        return helper.getHostName();
+    }
+
+    @Override
+    public String getOSName() {
+        return helper.getOSName();
+    }
+
+    @Override
+    public String getOSVersion() {
+        return helper.getOSVersion();
+    }
+
+    @Override
+    public String getCPUModel() {
+        return helper.getCPUModel();
+    }
+
+    @Override
+    public int getCPUCount() {
+        return helper.getCPUCount();
+    }
+
+    @Override
+    public long getTotalMemory() {
+        return helper.getTotalMemory();
+    }
+
+    @Override
+    public long getClockTicksPerSecond() {
+        return helper.getClockTicksPerSecond();
+    }
+
+    @Override
+    public PortableMemoryStat getMemoryStat() {
+        return new MacOSMemoryStat();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/macos/MacOSMemoryStat.java	Mon Jan 30 13:06:07 2017 -0500
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.common.portability.internal.macos;
+
+import com.redhat.thermostat.common.portability.PortableMemoryStat;
+
+class MacOSMemoryStat extends PortableMemoryStat {
+
+    MacOSMemoryStat() {
+        this(MacOSHelperImpl.INSTANCE.getMemoryInfo());
+    }
+
+    //     public PortableMemoryStat(long timeStamp, long total, long free, long buffers, long cached, long swapTotal, long swapFree, long commitLimit)
+    private MacOSMemoryStat(final long[] info) {
+        super(System.currentTimeMillis(), info[1], info[2], 0, 0, info[3], info[4], 0);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/macos/MacOSProcessImpl.java	Mon Jan 30 13:06:07 2017 -0500
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.common.portability.internal.macos;
+
+import com.redhat.thermostat.common.Clock;
+import com.redhat.thermostat.common.portability.PortableProcess;
+import com.redhat.thermostat.common.portability.PortableProcessStat;
+import com.redhat.thermostat.common.portability.PortableVmIoStat;
+
+import java.util.Map;
+
+public class MacOSProcessImpl implements PortableProcess {
+
+    public static final MacOSProcessImpl INSTANCE = new MacOSProcessImpl();
+    private static final MacOSHelperImpl helper = MacOSHelperImpl.INSTANCE;
+
+    @Override
+    public boolean exists(int pid) {
+        return helper.exists(pid);
+    }
+
+    @Override
+    public String getUserName(int pid) {
+        return helper.getUserName(pid);
+    }
+
+    @Override
+    public int getUid(int pid) {
+        return helper.getUid(pid);
+    }
+
+    @Override
+    public Map<String, String> getEnvironment(int pid) {
+        return helper.getEnvironment(pid);
+    }
+
+    @Override
+    public PortableProcessStat getProcessStat(int pid) {
+        final long[] info = helper.getProcessCPUInfo(pid);
+        final long utime = info[1];
+        final long stime = info[2];
+        return new PortableProcessStat(pid, utime, stime);
+    }
+
+    @Override
+    public PortableVmIoStat getVmIoStat(Clock clock, int pid) {
+        return new MacOSVmIoStat(clock, pid);
+    }
+
+    @Override
+    public boolean terminateProcess(int pid) {
+        return helper.terminateProcess(pid);
+    }
+
+    @Override
+    public boolean terminateProcess(int pid, boolean wait) {
+        return helper.terminateProcess(pid, wait);
+    }
+
+    @Override
+    public boolean terminateProcess(int pid, int exitcode, int waitMillis) {
+        return helper.terminateProcess(pid, exitcode, waitMillis);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/macos/MacOSUserInfoBuilderImpl.java	Mon Jan 30 13:06:07 2017 -0500
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.common.portability.internal.macos;
+
+import com.redhat.thermostat.common.portability.PortableProcess;
+import com.redhat.thermostat.common.portability.PortableProcessImpl;
+import com.redhat.thermostat.common.portability.ProcessUserInfo;
+import com.redhat.thermostat.common.portability.ProcessUserInfoBuilder;
+import com.redhat.thermostat.common.utils.LoggingUtils;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Build User information via Windows helper classes
+ */
+public class MacOSUserInfoBuilderImpl implements ProcessUserInfoBuilder {
+
+    private final PortableProcess procHelper;
+
+    private static final ProcessUserInfo NON_EXISTENT_USER = new ProcessUserInfo();
+    private static final Logger logger = LoggingUtils.getLogger(MacOSUserInfoBuilderImpl.class);
+
+    public MacOSUserInfoBuilderImpl() {
+        this(PortableProcessImpl.getInstance());
+    }
+
+    MacOSUserInfoBuilderImpl(PortableProcess helper) {
+        this.procHelper = helper;
+    }
+
+    @Override
+    public ProcessUserInfo build(int pid) {
+        ProcessUserInfo info = NON_EXISTENT_USER;
+        try {
+            final long uid = procHelper.getUid(pid);
+            final String name = procHelper.getUserName(pid);
+            info = new ProcessUserInfo(uid, name);
+        } catch (Exception e) {
+            logger.log(Level.WARNING, "Unable to read user info for " + pid, e);
+        }
+
+        return info;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/macos/MacOSVmIoStat.java	Mon Jan 30 13:06:07 2017 -0500
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.common.portability.internal.macos;
+
+import com.redhat.thermostat.common.Clock;
+import com.redhat.thermostat.common.portability.PortableVmIoStat;
+
+class MacOSVmIoStat extends PortableVmIoStat {
+
+    MacOSVmIoStat(Clock clock, int pid) {
+        this(clock, MacOSHelperImpl.INSTANCE.getProcessIOInfo(pid));
+    }
+
+    private MacOSVmIoStat(Clock clock, final long info[]) {
+        super(clock.getRealTimeMillis(), info[0], info[1], info[2], info[3]);
+    }
+}
--- a/common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/windows/WindowsHelperImpl.java	Mon Jan 30 10:30:19 2017 -0500
+++ b/common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/windows/WindowsHelperImpl.java	Mon Jan 30 13:06:07 2017 -0500
@@ -129,17 +129,22 @@
     }
 
     long[] getMemoryInfo() {
+        /**
+         *
+         data[0] = statex.dwMemoryLoad;
+         data[1] = statex.ullTotalPhys;
+         data[2] = statex.ullAvailPhys;
+         data[3] = statex.ullTotalPageFile;
+         data[4] = statex.ullAvailPageFile;
+         data[5] = statex.ullTotalVirtual;
+         data[6] = statex.ullAvailVirtual;
+         data[7] = statex.ullAvailExtendedVirtual;
+         */
         final long[] mi = new long[8];
         getGlobalMemoryStatus0(mi);
         return mi;
     }
 
-    private long[] getPerformanceInfo() {
-        final long[] mi = new long[13];
-        getPerformanceInfo0(mi);
-        return mi;
-    }
-
     public long getClockTicksPerSecond() {
         // we "know" this is 10E7 (units from  getProcessStat)
         // but calculate it anyways from current process
@@ -235,7 +240,6 @@
     private static native String getHostName0(boolean prependDomain);
     private static native void getOSVersion0(long[] versionAndBuild);
     private static native boolean getGlobalMemoryStatus0(long[] info);
-    private static native boolean getPerformanceInfo0(long[] info);
     private static native String getCPUString0();
     private static native int getCPUCount0();
     private static native long queryPerformanceFrequency0();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/portability/src/main/native/MacOSHelperImpl.c	Mon Jan 30 13:06:07 2017 -0500
@@ -0,0 +1,320 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+#include "com_redhat_thermostat_common_portability_internal_macos_MacOSHelperImpl.h"
+#include <jni.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <netdb.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <mach/vm_statistics.h>
+
+#define MAX_NAME 256
+#ifndef NI_MAXHOST
+#define NI_MAXHOST 1025
+#endif /* NI_MAXHOST */
+
+#if defined(DEBUG)
+static void testLength(JNIEnv* env, jlongArray array, int minLength) {
+    // sanity test
+    jsize len = (*env)->GetArrayLength(env, array);
+    assert(len >= minLength);
+}
+#else
+static void testLength(JNIEnv* env, jlongArray array, int minLength) {}
+#endif
+
+#if !defined(TRUE)
+# define TRUE 1
+# define FALSE 0
+#endif
+
+static jint throw_IOException(JNIEnv *env, const char *message) {
+    const char *class_name = "java/io/IOException";
+    jclass class = (*env)->FindClass(env, class_name);
+    if (class == NULL) {
+        return -1;
+    }
+    return (*env)->ThrowNew(env, class, message);
+}
+
+/*
+ * Class:     com_redhat_thermostat_common_portability_internal_macos_MacOSHelperImpl
+ * Method:    getHostName0
+ * Signature: ()Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL
+Java_com_redhat_thermostat_common_portability_internal_macos_MacOSHelperImpl_getHostName0
+  (JNIEnv *env, jclass winHelperClass)
+{
+      char hostname[NI_MAXHOST];
+      memset(hostname, 0, sizeof(hostname));
+
+      if (gethostname(hostname,  sizeof(hostname)) == 0) {
+          return (*env)->NewStringUTF(env, hostname);
+      }
+      return NULL;
+}
+
+/*
+ * Class:     com_redhat_thermostat_common_portability_internal_macos_MacOSHelperImpl
+ * Method:    getGlobalMemoryStatus0
+ * Signature: ([J)V
+ */
+JNIEXPORT jboolean JNICALL Java_com_redhat_thermostat_common_portability_internal_macos_MacOSHelperImpl_getGlobalMemoryStatus0
+  (JNIEnv *env, jclass winHelperClass, jlongArray array)
+{
+    testLength(env, array, 8);
+
+    // Get the element pointer
+    jlong* data = (*env)->GetLongArrayElements(env, array, 0);
+
+
+    (*env)->ReleaseLongArrayElements(env, array, data, 0);
+    return TRUE;
+}
+
+/*
+ * Class:     com_redhat_thermostat_common_portability_internal_macos_MacOSHelperImpl
+ * Method:    getPerformanceInfo0
+ * Signature: ([J)V
+ */
+JNIEXPORT jboolean JNICALL Java_com_redhat_thermostat_common_portability_internal_macos_MacOSHelperImpl_getPerformanceInfo0
+  (JNIEnv *env, jclass winHelperClass, jlongArray array)
+{
+    testLength(env, array, 13);
+
+    // Get the element pointer
+    jlong* data = (*env)->GetLongArrayElements(env, array, 0);
+
+    (*env)->ReleaseLongArrayElements(env, array, data, 0);
+    return TRUE;
+}
+
+/*
+ * Class:     com_redhat_thermostat_common_portability_internal_macos_MacOSHelperImpl
+ * Method:    queryPerformanceFrequency0
+ * Signature: ()I
+ */
+JNIEXPORT jlong JNICALL Java_com_redhat_thermostat_common_portability_internal_macos_MacOSHelperImpl_queryPerformanceFrequency0
+  (JNIEnv *env, jclass winHelperClass)
+{
+    return (jlong)(9999);
+}
+
+uid_t uidFromPid(pid_t pid)
+{
+    uid_t uid = -1;
+
+    struct kinfo_proc process;
+    size_t procBufferSize = sizeof(process);
+
+    // Compose search path for sysctl. Here you can specify PID directly.
+    const u_int pathLenth = 4;
+    int path[pathLenth] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
+
+    int sysctlResult = sysctl(path, pathLenth, &process, &procBufferSize, NULL, 0);
+
+    // If sysctl did not fail and process with PID available - take UID.
+    if ((sysctlResult == 0) && (procBufferSize != 0))
+    {
+        uid = process.kp_eproc.e_ucred.cr_uid;
+    }
+
+    return uid;
+}
+
+/*
+ * Class:     com_redhat_thermostat_common_portability_internal_macos_MacOSHelperImpl
+ * Method:    getUserName0
+ * Signature: (I)Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL
+Java_com_redhat_thermostat_common_portability_internal_macos_MacOSHelperImpl_getUserName0
+  (JNIEnv *env, jclass winHelperClass, jint pid)
+{
+    uid_t uid = uidFromPid(pid);
+
+    struct passwd pwdbuf;
+    struct passwd* pwdPtr;
+    char buf[1024];
+
+    int rc = getpwuid_r(uid, &pwdbuf, buf, sizeof(buf), &pwdPtr);
+
+    if (rc) {
+        throw_IOException(env, "getpwuid_r() error");
+    }
+    else if (pwdPtr == NULL) {
+        // no such entry
+        return NULL;
+    }
+    return (*env)->NewStringUTF(env, pwdbuf.pw_name);
+}
+
+/*
+ * Class:     com_redhat_thermostat_common_portability_internal_macos_MacOSHelperImpl
+ * Method:    getEnvironment0
+ * Signature: ()[Ljava/lang/String;
+ */
+JNIEXPORT jobjectArray JNICALL
+Java_com_redhat_thermostat_common_portability_internal_macos_MacOSHelperImpl_getEnvironment0
+  (JNIEnv *env, jclass winHelperClass, jint pid)
+{
+    // TODO - implement this stub - not eay (have to open the process memory and poke around)
+    // for now, return an empty array
+    jobjectArray ret = (jobjectArray)(*env)->NewObjectArray(env, 0, (*env)->FindClass(env, "java/lang/String"), (*env)->NewStringUTF(env, ""));
+    return ret;
+}
+
+/*
+ * Class:     com_redhat_thermostat_common_portability_internal_macos_MacOSHelperImpl
+ * Method:    getProcessMemoryInfo0
+ * Signature: (I[J)V
+ */
+JNIEXPORT jboolean JNICALL Java_com_redhat_thermostat_common_portability_internal_macos_MacOSHelperImpl_getProcessInfo0
+  (JNIEnv *env, jclass winHelperClass, jint pid, jlongArray array)
+{
+    testLength(env, array, 4);
+
+    // Get the element pointer
+    jlong* data = (*env)->GetLongArrayElements(env, array, 0);
+    (*env)->ReleaseLongArrayElements(env, array, data, 0);
+    return TRUE;
+}
+
+/*
+ * Class:     com_redhat_thermostat_common_portability_internal_macos_MacOSHelperImpl
+ * Method:    getProcessIOInfo0
+ * Signature: (I[J)V
+ */
+JNIEXPORT jboolean JNICALL Java_com_redhat_thermostat_common_portability_internal_macos_MacOSHelperImpl_getProcessIOInfo0
+  (JNIEnv *env, jclass winHelperClass, jint pid, jlongArray array)
+{
+    testLength(env, array, 6);
+
+    // Get the element pointer
+    jlong* data = (*env)->GetLongArrayElements(env, array, 0);
+    (*env)->ReleaseLongArrayElements(env, array, data, 0);
+    return TRUE;
+}
+
+/*
+ * Class:     com_redhat_thermostat_common_portability_internal_macos_MacOSHelperImpl
+ * Method:    getProcessHandle0
+ * Signature: (I)J
+ */
+JNIEXPORT jlong JNICALL Java_com_redhat_thermostat_common_portability_internal_macos_MacOSHelperImpl_getCurrentProcessPid0
+  (JNIEnv *env, jclass winHelperClass) {
+
+    return (jlong) getpid();
+}
+
+/*
+ * Class:     com_redhat_thermostat_common_portability_internal_macos_MacOSHelperImpl
+ * Method:    getProcessHandle0
+ * Signature: (I)J
+ */
+JNIEXPORT jlong JNICALL Java_com_redhat_thermostat_common_portability_internal_macos_MacOSHelperImpl_getProcessUid0
+  (JNIEnv *env, jclass winHelperClass, jint pid) {
+    return (jlong) uidFromPid(pid);
+}
+
+/*
+ * Class:     com_redhat_thermostat_common_portability_internal_macos_MacOSHelperImpl
+ * Method:    terminateProcess0
+ * Signature: (IIB)V
+ */
+JNIEXPORT jboolean JNICALL Java_com_redhat_thermostat_common_portability_internal_macos_MacOSHelperImpl_terminateProcess0
+  (JNIEnv *env, jclass winHelperClass, jint pid, jint exitCode, jint waitMillis) {
+
+    return TRUE;
+}
+
+/*
+ * Class:     com_redhat_thermostat_common_portability_internal_macos_MacOSHelperImpl
+ * Method:    getLongSysctl0
+ * Signature: (Ljava/lang/String;)J
+ */
+JNIEXPORT jlong JNICALL Java_com_redhat_thermostat_common_portability_internal_macos_MacOSHelperImpl_getLongSysctl0
+  (JNIEnv *env, jclass winHelperClass, jstring info) {
+
+    const char* info_cstr = (*env)->GetStringUTFChars(env, info, NULL);
+
+    jlong ret = 0;
+    size_t size = sizeof(ret);
+
+    if (sysctlbyname(info_cstr, &ret, &size, NULL, 0) < 0) {
+        throw_IOException(env, "bad sysctl() string");
+    }
+
+    return ret;
+}
+
+/*
+ * Class:     com_redhat_thermostat_common_portability_internal_macos_MacOSHelperImpl
+ * Method:    getStringSysctl0
+ * Signature: (Ljava/lang/String;)Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL Java_com_redhat_thermostat_common_portability_internal_macos_MacOSHelperImpl_getStringSysctl0
+  (JNIEnv *env, jclass winHelperClass, jstring info) {
+
+    const char* info_cstr = (*env)->GetStringUTFChars(env, info, NULL);
+
+    size_t size = 0;
+
+    if (sysctlbyname(info_cstr, NULL, &size, NULL, 0) < 0) {
+        throw_IOException(env, "bad sysctl() string");
+    }
+
+    char* buffer = malloc(size);
+
+    if (sysctlbyname(info_cstr, buffer, &size, NULL, 0) < 0) {
+        throw_IOException(env, "bad sysctl() string");
+    }
+
+    jstring ret = (*env)->NewStringUTF(env, buffer);
+
+    free(buffer);
+
+    return ret;
+}
+
+
--- a/common/portability/src/main/native/WindowsHelperImpl.c	Mon Jan 30 10:30:19 2017 -0500
+++ b/common/portability/src/main/native/WindowsHelperImpl.c	Mon Jan 30 13:06:07 2017 -0500
@@ -134,58 +134,6 @@
     return TRUE;
 }
 
-/*
- * Class:     com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl
- * Method:    getPerformanceInfo0
- * Signature: ([J)V
- */
-JNIEXPORT boolean JNICALL Java_com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl_getPerformanceInfo0
-  (JNIEnv *env, jclass winHelperClass, jlongArray array)
-{
-    testLength(env, array, 13);
-
-    // Get the element pointer
-    jlong* data = (*env)->GetLongArrayElements(env, array, 0);
-
-    /**
-     // from PERFORMANCE_INFORMATION (13 values)
-        SIZE_T CommitTotal;
-        SIZE_T CommitLimit;
-        SIZE_T CommitPeak;
-        SIZE_T PhysicalTotal;
-        SIZE_T PhysicalAvailable;
-        SIZE_T SystemCache;
-        SIZE_T KernelTotal;
-        SIZE_T KernelPaged;
-        SIZE_T KernelNonpaged;
-        SIZE_T PageSize;
-        DWORD  HandleCount;
-        DWORD  ProcessCount;
-        DWORD  ThreadCount;
-     */
-
-    // get the memeory info
-    PERFORMANCE_INFORMATION statex;
-    statex.cb = sizeof(statex);
-    GetPerformanceInfo(&statex, statex.cb);
-    data[0] = statex.CommitTotal;
-    data[1] = statex.CommitLimit;
-    data[2] = statex.CommitPeak;
-    data[3] = statex.PhysicalTotal;
-    data[4] = statex.PhysicalAvailable;
-    data[5] = statex.SystemCache;
-    data[6] = statex.KernelTotal;
-    data[7] = statex.KernelPaged;
-    data[8] = statex.KernelNonpaged;
-    data[9] = statex.PageSize;
-    data[10] = statex.HandleCount;
-    data[11] = statex.ProcessCount;
-    data[12] = statex.ThreadCount;
-
-    (*env)->ReleaseLongArrayElements(env, array, data, 0);
-    return TRUE;
-}
-
 
 /*
  * Class:     com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl
--- a/distribution/config/thermostatrc	Mon Jan 30 10:30:19 2017 -0500
+++ b/distribution/config/thermostatrc	Mon Jan 30 13:06:07 2017 -0500
@@ -48,7 +48,7 @@
 #
 #JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk
 
-if [ "$(expr substr $(uname -s) 1 6)" == "CYGWIN" ]; then
+if [ "$(uname -s | cut -b1-6)" == "CYGWIN" ]; then
   ##echo "Running under Cygwin"
   export CYGWIN_MODE=1
 else
--- a/distribution/pom.xml	Mon Jan 30 10:30:19 2017 -0500
+++ b/distribution/pom.xml	Mon Jan 30 13:06:07 2017 -0500
@@ -73,7 +73,7 @@
     <profile>
       <id>linux</id>
       <activation>
-        <os><family>Unix</family></os>
+        <os><name>linux</name></os>
       </activation>
       <properties>
         <assemblyfile.suffix/>
@@ -111,13 +111,88 @@
                 <phase>prepare-package</phase>
                 <configuration>
                   <target>
-                    <copy file="${main.basedir}/keyring/target/libGnomeKeyringWrapper.so"
+                    <copy file="${main.basedir}/keyring/target/libGnomeKeyringWrapper${sharedlib.suffix}"
+                          todir="${project.build.directory}/image/libs/native" />
+                    <copy file="${main.basedir}/common/portability/target/libHostNameWrapper${sharedlib.suffix}"
                           todir="${project.build.directory}/image/libs/native" />
-                    <copy file="${main.basedir}/common/portability/target/libHostNameWrapper.so"
+                    <copy file="${main.basedir}/common/portability/target/libUserNameUtilWrapper${sharedlib.suffix}"
+                          todir="${project.build.directory}/image/libs/native" />
+                    <copy file="${main.basedir}/laf-utils/target/libGTKThemeUtils${sharedlib.suffix}"
                           todir="${project.build.directory}/image/libs/native" />
-                    <copy file="${main.basedir}/common/portability/target/libUserNameUtilWrapper.so"
+                  </target>
+                </configuration>
+                <goals>
+                  <goal>run</goal>
+                </goals>
+              </execution>
+            </executions>
+          </plugin>
+          <plugin>
+            <groupId>org.codehaus.mojo</groupId>
+            <artifactId>exec-maven-plugin</artifactId>
+            <executions>
+              <execution>
+                <phase>integration-test</phase>
+                <goals>
+                  <goal>exec</goal>
+                </goals>
+              </execution>
+            </executions>
+            <configuration>
+              <executable>${project.basedir}/tools/verify-bash-completion.sh</executable>
+            </configuration>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+
+
+    <profile>
+      <id>macos</id>
+      <activation>
+        <os><family>mac</family></os>
+      </activation>
+      <properties>
+        <assemblyfile.suffix/>
+        <agent.extra.bundles>,com.redhat.thermostat.agent.ipc.unixsocket.server=${project.version}, \
+          ${jffi-native.bundle.symbolic.name}=${jffi.version}</agent.extra.bundles>
+        <service.extra.bundles>,com.redhat.thermostat.agent.ipc.unixsocket.server=${project.version}, \
+          ${jffi-native.bundle.symbolic.name}=${jffi.version}</service.extra.bundles>
+      </properties>
+      <dependencies>
+        <dependency>
+          <groupId>com.redhat.thermostat</groupId>
+          <artifactId>thermostat-keyring</artifactId>
+          <version>${project.version}</version>
+        </dependency>
+        <dependency>
+          <groupId>com.redhat.thermostat</groupId>
+          <artifactId>thermostat-agent-ipc-unixsocket-server</artifactId>
+          <version>${project.version}</version>
+        </dependency>
+        <dependency>
+          <groupId>com.redhat.thermostat</groupId>
+          <artifactId>thermostat-agent-ipc-unixsocket-client</artifactId>
+          <version>${project.version}</version>
+        </dependency>
+      </dependencies>
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-antrun-plugin</artifactId>
+            <executions>
+              <!--linux only files -->
+              <execution>
+                <id>directory-structure-linux</id>
+                <phase>prepare-package</phase>
+                <configuration>
+                  <target>
+                    <copy file="${main.basedir}/common/portability/target/libHostNameWrapper${sharedlib.suffix}"
                           todir="${project.build.directory}/image/libs/native" />
-                    <copy file="${main.basedir}/laf-utils/target/libGTKThemeUtils.so"
+                    <copy file="${main.basedir}/common/portability/target/libUserNameUtilWrapper${sharedlib.suffix}"
+                          todir="${project.build.directory}/image/libs/native" />
+                    <copy file="${main.basedir}/common/portability/target/libMacOSHelperWrapper${sharedlib.suffix}"
                           todir="${project.build.directory}/image/libs/native" />
                   </target>
                 </configuration>
@@ -169,11 +244,11 @@
                 <configuration>
                   <target>
                     <!--  copy and rename the native libraries -->
-                    <copy file="${main.basedir}/common/portability/target/HostNameWrapper.dll"
+                    <copy file="${main.basedir}/common/portability/target/HostNameWrapper${sharedlib.suffix}"
                           todir="${project.build.directory}/image/libs/native" />
-                    <copy file="${main.basedir}/common/portability/target/UserNameUtilWrapper.dll"
+                    <copy file="${main.basedir}/common/portability/target/UserNameUtilWrapper${sharedlib.suffix}"
                           todir="${project.build.directory}/image/libs/native" />
-                    <copy file="${main.basedir}/common/portability/target/WindowsHelperWrapper.dll"
+                    <copy file="${main.basedir}/common/portability/target/WindowsHelperWrapper${sharedlib.suffix}"
                           todir="${project.build.directory}/image/libs/native" />
                   </target>
                 </configuration>
--- a/distribution/scripts/thermostat-command-channel	Mon Jan 30 10:30:19 2017 -0500
+++ b/distribution/scripts/thermostat-command-channel	Mon Jan 30 13:06:07 2017 -0500
@@ -78,14 +78,19 @@
 
 # Determine owner of this file
 THIS_SCRIPT="$CWD/thermostat-command-channel"
-SCRIPT_OWNER=$(stat -c '%U' "${THIS_SCRIPT}")
+if [ $DARWIN_MODE -eq 0 ]; then
+  SCRIPT_OWNER=$(stat -c '%U' "${THIS_SCRIPT}")
+else
+  # posix
+  SCRIPT_OWNER=$(stat -f '%Su' "${THIS_SCRIPT}")
+fi
 
 # Start server
 if [ $CYGWIN_MODE -eq 1 ]; then
 	CONFIG_FILE_ARG="-DipcConfigFile=`cygpath -w ${CONFIG_FILE}`"
 	# Drop permissions, if root
 	if [ "$(id -u)" -eq 0 ]; then
-	  exec /bin/su -s /bin/bash -c "${JAVA} ${CONFIG_FILE_ARG} ${LOGGING_ARGS} -cp `cygpath -w -p ${IPC_CLASSPATH}` ${DEBUG_OPTS} ${CMD_CHANNEL_CLASS} ${HOSTNAME} ${PORT}" "${SCRIPT_OWNER}" 
+	  exec /bin/su -s /bin/bash -c "${JAVA} ${CONFIG_FILE_ARG} ${LOGGING_ARGS} -cp `cygpath -w -p ${IPC_CLASSPATH}` ${DEBUG_OPTS} ${CMD_CHANNEL_CLASS} ${HOSTNAME} ${PORT}" "${SCRIPT_OWNER}"
 	else
 	  exec ${JAVA} "${CONFIG_FILE_ARG}" ${LOGGING_ARGS} -cp "`cygpath -w -p ${IPC_CLASSPATH}`" ${DEBUG_OPTS} "${CMD_CHANNEL_CLASS}" "${HOSTNAME}" "${PORT}"
 	fi
@@ -93,7 +98,7 @@
 	CONFIG_FILE_ARG="-DipcConfigFile=${CONFIG_FILE}"
 	# Drop permissions, if root
 	if [ "$(id -u)" -eq 0 ]; then
-	  exec /bin/su -s /bin/bash -c "${JAVA} ${CONFIG_FILE_ARG} ${LOGGING_ARGS} -cp ${IPC_CLASSPATH} ${DEBUG_OPTS} ${CMD_CHANNEL_CLASS} ${HOSTNAME} ${PORT}" "${SCRIPT_OWNER}" 
+	  exec /bin/su -s /bin/bash -c "${JAVA} ${CONFIG_FILE_ARG} ${LOGGING_ARGS} -cp ${IPC_CLASSPATH} ${DEBUG_OPTS} ${CMD_CHANNEL_CLASS} ${HOSTNAME} ${PORT}" "${SCRIPT_OWNER}"
 	else
 	  exec ${JAVA} "${CONFIG_FILE_ARG}" ${LOGGING_ARGS} -cp ${IPC_CLASSPATH} ${DEBUG_OPTS} "${CMD_CHANNEL_CLASS}" "${HOSTNAME}" "${PORT}"
 	fi
--- a/distribution/scripts/thermostat-common	Mon Jan 30 10:30:19 2017 -0500
+++ b/distribution/scripts/thermostat-common	Mon Jan 30 13:06:07 2017 -0500
@@ -51,11 +51,11 @@
 }
 
 # set global variable for Cygwin testing
-# between all shell files, we pass cygwin-compatible paths, 
+# between all shell files, we pass cygwin-compatible paths,
 #    and let each script decide when to convert them.
 # an exception is command line args for java programs, which
 #    need to be converted to windows format at creation time
-if [ "$(expr substr $(uname -s) 1 6)" == "CYGWIN" ]; then
+if [ "$(uname -s | cut -b1-6)" == "CYGWIN" ]; then
   ##echo "Running under Cygwin"
   export CYGWIN_MODE=1
 else
@@ -63,6 +63,14 @@
   export CYGWIN_MODE=0
 fi
 
+if [ "$(uname -s | cut -b1-6)" == "Darwin" ]; then
+  ##echo "Running under Darwin"
+  export DARWIN_MODE=1
+else
+  ##echo "Running under Linux"
+  export DARWIN_MODE=0
+fi
+
 # Thermostat home
 if [[ "${THERMOSTAT_HOME}" = "" ]]; then
   THERMOSTAT_HOME="$(_find_thermostat_home)"
@@ -103,7 +111,7 @@
 fi
 
 # Source system thermostat profile
-. ${THERMOSTAT_HOME}/etc/thermostatrc 
+. ${THERMOSTAT_HOME}/etc/thermostatrc
 # Source user thermostat profile (if any)
 if [[ -f ${USER_THERMOSTAT_HOME}/etc/thermostatrc ]]; then
   . ${USER_THERMOSTAT_HOME}/etc/thermostatrc
--- a/keyring/pom.xml	Mon Jan 30 10:30:19 2017 -0500
+++ b/keyring/pom.xml	Mon Jan 30 13:06:07 2017 -0500
@@ -43,12 +43,13 @@
     <groupId>com.redhat.thermostat</groupId>
     <version>1.99.12-SNAPSHOT</version>
   </parent>
-  
+
   <artifactId>thermostat-keyring</artifactId>
   <packaging>bundle</packaging>
   <name>Thermostat Keyring API</name>
 
   <profiles>
+
     <profile>
       <id>linux</id>
       <activation>
@@ -109,6 +110,7 @@
         </plugins>
       </build>
     </profile>
+
   </profiles>
 
   <build>
@@ -131,7 +133,7 @@
           </instructions>
         </configuration>
       </plugin>
-    
+
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-surefire-plugin</artifactId>
@@ -142,7 +144,7 @@
             </systemPropertyVariables>
           </configuration>
       </plugin>
-      
+
     </plugins>
     <pluginManagement>
 	  <plugins>
@@ -180,7 +182,7 @@
       <artifactId>junit</artifactId>
       <scope>test</scope>
     </dependency>
-    
+
     <dependency>
       <groupId>org.osgi</groupId>
       <artifactId>org.osgi.core</artifactId>
@@ -198,13 +200,13 @@
       <version>${project.version}</version>
       <scope>compile</scope>
     </dependency>
-    
+
     <dependency>
       <groupId>com.redhat.thermostat</groupId>
       <artifactId>thermostat-shared-config</artifactId>
       <version>${project.version}</version>
       <scope>compile</scope>
     </dependency>
-    
+
   </dependencies>
 </project>
--- a/laf-utils/pom.xml	Mon Jan 30 10:30:19 2017 -0500
+++ b/laf-utils/pom.xml	Mon Jan 30 13:06:07 2017 -0500
@@ -43,7 +43,7 @@
     <groupId>com.redhat.thermostat</groupId>
     <version>1.99.12-SNAPSHOT</version>
   </parent>
-  
+
   <artifactId>thermostat-laf-utils</artifactId>
   <packaging>bundle</packaging>
   <name>Thermostat Look And Feel Utils</name>
@@ -114,9 +114,9 @@
           </instructions>
         </configuration>
       </plugin>
-     
+
     </plugins>
-    
+
     <pluginManagement>
 	  <plugins>
         <!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.-->
@@ -153,7 +153,7 @@
       <artifactId>junit</artifactId>
       <scope>test</scope>
     </dependency>
-    
+
     <dependency>
       <groupId>org.osgi</groupId>
       <artifactId>org.osgi.core</artifactId>
@@ -169,7 +169,7 @@
       <artifactId>thermostat-shared-config</artifactId>
       <version>${project.version}</version>
     </dependency>
-    
+
     <dependency>
       <groupId>com.redhat.thermostat</groupId>
       <artifactId>thermostat-client-core</artifactId>
--- a/pom.xml	Mon Jan 30 10:30:19 2017 -0500
+++ b/pom.xml	Mon Jan 30 13:06:07 2017 -0500
@@ -74,6 +74,21 @@
     </profile>
 
     <profile>
+      <id>macos</id>
+      <activation>
+        <os><family>mac</family></os>
+      </activation>
+      <properties>
+        <script.extension>.sh</script.extension>
+        <c.compiler>gcc</c.compiler>
+        <cflags/>
+        <jni.platform>darwin</jni.platform>
+        <sharedlib.prefix>lib</sharedlib.prefix>
+        <sharedlib.suffix>.dylib</sharedlib.suffix>
+      </properties>
+    </profile>
+
+    <profile>
       <id>windows</id>
       <activation>
         <os><family>Windows</family></os>
@@ -84,7 +99,7 @@
         <cflags>-std=c99</cflags>
         <cygwin.dir>c:/cygwin64</cygwin.dir>
         <jni.platform>win32</jni.platform>
-        <sharedlib.prefix></sharedlib.prefix>
+        <sharedlib.prefix/>
         <sharedlib.suffix>.dll</sharedlib.suffix>
       </properties>
     </profile>
@@ -109,7 +124,7 @@
 
     <!-- Profile for running performance tests. Performance tests are
          excluded from normal builds. That is, they'll only run if
-         explicitly requested via -Pperf-tests. See web/common/pom.xml 
+         explicitly requested via -Pperf-tests. See web/common/pom.xml
          for an example as to how this property is used. -->
     <profile>
       <id>perf-tests</id>
@@ -211,9 +226,9 @@
   </profiles>
 
   <properties>
-  
-    <main.basedir>${project.basedir}</main.basedir>  
-  
+
+    <main.basedir>${project.basedir}</main.basedir>
+
     <maven.build.timestamp.format>yyyy-MM-dd</maven.build.timestamp.format>
     <thermostat.releasedate>${maven.build.timestamp}</thermostat.releasedate>
     <thermostat.email>thermostat@icedtea.classpath.org</thermostat.email>
@@ -265,7 +280,7 @@
     <!-- the OSGi Bundle-Version; should match the manifest in the jar -->
     <commons-codec.osgi-version>1.7.0</commons-codec.osgi-version>
     <commons-fileupload.version>1.2.2</commons-fileupload.version>
-    
+
     <!-- jnr-unixsocket and dependencies -->
     <jnr-unixsocket.bundle.symbolic.name>com.github.jnr.unixsocket</jnr-unixsocket.bundle.symbolic.name>
     <jnr-unixsocket.version>0.12</jnr-unixsocket.version>
@@ -748,7 +763,6 @@
         <artifactId>gson</artifactId>
         <version>${gson.version}</version>
       </dependency>
-    
       <!-- jnr-unixsocket and dependencies -->
       <dependency>
         <groupId>com.github.jnr</groupId>
--- a/process-handler/src/main/java/com/redhat/thermostat/service/internal/unix/UnixProcessUtilities.java	Mon Jan 30 10:30:19 2017 -0500
+++ b/process-handler/src/main/java/com/redhat/thermostat/service/internal/unix/UnixProcessUtilities.java	Mon Jan 30 13:06:07 2017 -0500
@@ -38,6 +38,7 @@
 
 import com.redhat.thermostat.service.internal.ProcessUtilitiesBase;
 import com.redhat.thermostat.service.process.UNIXSignal;
+import com.redhat.thermostat.shared.config.OS;
 
 import java.io.BufferedReader;
 import java.io.IOException;
@@ -55,7 +56,10 @@
     protected List<String> buildCommandLine(Integer pid) {
         final List<String> commandLine = new ArrayList<>();
         commandLine.add("ps");
-        commandLine.add("--no-heading");
+        if (!OS.IS_MACOS)
+            commandLine.add("--no-heading");
+        else
+            commandLine.add("-ocomm=");
         commandLine.add("-p");
         commandLine.add(String.valueOf(pid));
         return commandLine;