changeset 2764:d624d92d95ca

jvm-memory should also collect resident memory Reviewed-by: stooke Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2017-September/025132.html
author Miloslav Zezulka <mzezulka@redhat.com>
date Wed, 20 Sep 2017 12:23:54 +0200
parents 845c5af7b42f
children ef101bb72531
files common/portability/src/main/java/com/redhat/thermostat/common/portability/PortableProcess.java common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/linux/LinuxNativeLibsExtractor.java common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/linux/LinuxPortableProcessImpl.java common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/linux/LinuxProcfsDataExtractor.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/windows/WindowsPortableProcessImpl.java common/portability/src/main/java/com/redhat/thermostat/common/portability/linux/ProcDataSource.java common/portability/src/test/java/com/redhat/thermostat/common/portability/internal/linux/LinuxNativeLibsExtractorTest.java common/portability/src/test/java/com/redhat/thermostat/common/portability/internal/linux/LinuxProcfsDataExtractorTest.java common/portability/src/test/resources/native_lib_empty common/portability/src/test/resources/native_lib_no_libs common/portability/src/test/resources/native_lib_three_libs plugins/jvm-overview/agent/src/main/java/com/redhat/thermostat/jvm/overview/agent/internal/model/VmNativeLibsExtractorFactory.java plugins/vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryBackend.java plugins/vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryResidentSizeExtractor.java plugins/vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryResidentSizeExtractorImpl.java plugins/vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryVmListener.java plugins/vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/model/VmMemoryStat.java plugins/vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryBackendTest.java plugins/vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryVmListenerTest.java plugins/vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/models/VmMemoryStatDAOImplTest.java
diffstat 21 files changed, 586 insertions(+), 282 deletions(-) [+]
line wrap: on
line diff
--- a/common/portability/src/main/java/com/redhat/thermostat/common/portability/PortableProcess.java	Tue Sep 26 15:10:31 2017 +0200
+++ b/common/portability/src/main/java/com/redhat/thermostat/common/portability/PortableProcess.java	Wed Sep 20 12:23:54 2017 +0200
@@ -63,4 +63,11 @@
     int getCurrentProcessPid();
 
     String[] getNativeLibs(int pid);
+
+    /**
+     * Returns resident memory size of the process with PID {@code pid}. If no
+     * such information is available or cannot be retrieved, null is
+     * returned.
+     */
+    Long getResidentMemorySize(int pid);
 }
--- a/common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/linux/LinuxNativeLibsExtractor.java	Tue Sep 26 15:10:31 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,84 +0,0 @@
-/*
- * 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.linux;
-
-import com.redhat.thermostat.common.utils.LoggingUtils;
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileReader;
-import java.io.IOException;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-public class LinuxNativeLibsExtractor {
-
-    private static final Logger LOGGER
-            = LoggingUtils.getLogger(LinuxNativeLibsExtractor.class);
-
-    public static String[] getNativeLibs(int pid) {
-        return getNativeLibsFromReader(new File(String.format("/proc/%d/maps", pid)));
-    }
-
-    // for testing purposes only
-    static String[] getNativeLibs(File testFile) {
-        return getNativeLibsFromReader(testFile);
-    }
-
-    private static String[] getNativeLibsFromReader(File nativeLibFile) {
-        final String soGrep = ".+\\.so.*";
-        Set<String> result = new HashSet<>();
-        try (BufferedReader br
-                     = new BufferedReader(new FileReader(nativeLibFile))) {
-            String next = br.readLine();
-            while (next != null) {
-                next = next.trim();
-                if (next.matches(soGrep)) {
-                    String candidate = next.substring(next.lastIndexOf(' ') + 1);
-                    result.add(candidate);
-                }
-                next = br.readLine();
-            }
-            return result.toArray(new String[0]);
-        } catch (IOException ex) {
-            LOGGER.log(Level.WARNING, "Unable to retrieve native libraries.");
-            LOGGER.log(Level.INFO, ex.getMessage());
-            return new String[0];
-        }
-    }
-}
--- a/common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/linux/LinuxPortableProcessImpl.java	Tue Sep 26 15:10:31 2017 +0200
+++ b/common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/linux/LinuxPortableProcessImpl.java	Wed Sep 20 12:23:54 2017 +0200
@@ -122,6 +122,11 @@
 
     @Override
     public String[] getNativeLibs(int pid) {
-        return LinuxNativeLibsExtractor.getNativeLibs(pid);
+        return LinuxProcfsDataExtractor.getNativeLibs(pid);
+    }
+
+    @Override
+    public Long getResidentMemorySize(int pid) {
+        return LinuxProcfsDataExtractor.getResidentMemorySize(pid);
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/linux/LinuxProcfsDataExtractor.java	Wed Sep 20 12:23:54 2017 +0200
@@ -0,0 +1,134 @@
+/*
+/*
+ * 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.linux;
+
+import com.redhat.thermostat.common.portability.linux.ProcDataSource;
+import com.redhat.thermostat.common.utils.LoggingUtils;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class LinuxProcfsDataExtractor {
+
+    private static final Logger LOGGER
+            = LoggingUtils.getLogger(LinuxProcfsDataExtractor.class);
+    private static final ProcDataSource PROC_DATA_HELPER = new ProcDataSource();
+
+    /**
+     * Returns String array consisting of loaded native libraries using
+     * /proc/$PID/maps. Should an error occur or such file does not exist, empty
+     * array is returned.
+     */
+    public static String[] getNativeLibs(int pid) {
+        try {
+            return getNativeLibsFromFile(PROC_DATA_HELPER.getMapsFile(pid));
+        } catch (IOException ex) {
+            LOGGER.log(Level.WARNING, "Failed reading procfs maps file.");
+            LOGGER.log(Level.INFO, ex.getMessage());
+            return new String[0];
+        }
+    }
+
+    // for testing purposes only
+    static String[] getNativeLibs(StringReader testReader) {
+        return getNativeLibsFromFile(testReader);
+    }
+
+    private static String[] getNativeLibsFromFile(Reader reader) {
+        final String soGrep = ".+\\.so.*";
+        Set<String> result = new HashSet<>();
+        try (BufferedReader br
+                = new BufferedReader(reader)) {
+            String next = br.readLine();
+            while (next != null) {
+                next = next.trim();
+                if (next.matches(soGrep)) {
+                    String candidate = next.substring(next.lastIndexOf(' ') + 1);
+                    result.add(candidate);
+                }
+                next = br.readLine();
+            }
+            return result.toArray(new String[0]);
+        } catch (IOException ex) {
+            LOGGER.log(Level.WARNING, "Unable to retrieve native libraries.");
+            LOGGER.log(Level.INFO, ex.getMessage());
+            return new String[0];
+        }
+    }
+
+    public static Long getResidentMemorySize(int pid) {
+        try {
+            return getResidentMemorySizeFromFile(PROC_DATA_HELPER.getStatusReader(pid));
+        } catch (IOException ex) {
+            LOGGER.log(Level.WARNING, "Failed reading procfs status file.");
+            LOGGER.log(Level.INFO, ex.getMessage());
+            return null;
+        }
+    }
+
+    // for testing purposes only
+    static Long getResidentMemorySize(StringReader file) {
+        return getResidentMemorySizeFromFile(file);
+    }
+
+    private static Long getResidentMemorySizeFromFile(Reader reader) {
+        final String vmRssRegex = "VmRSS:\\s+.*";
+        try (BufferedReader br = new BufferedReader(reader)) {
+            String next = br.readLine();
+            while (next != null) {
+                next = next.trim();
+                if (next.matches(vmRssRegex)) {
+                    String residentMem = next.split("\\s+")[1];
+                    return Long.parseLong(residentMem);
+                }
+                next = br.readLine();
+            }
+            return null;
+        } catch (IOException ex) {
+            LOGGER.log(Level.WARNING, "Unable to retrieve resident memory size.");
+            LOGGER.log(Level.INFO, ex.getMessage());
+            return null;
+        }
+    }
+}
+
--- a/common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/macos/MacOSProcessImpl.java	Tue Sep 26 15:10:31 2017 +0200
+++ b/common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/macos/MacOSProcessImpl.java	Wed Sep 20 12:23:54 2017 +0200
@@ -42,11 +42,16 @@
 import com.redhat.thermostat.common.portability.PortableVmIoStat;
 import com.redhat.thermostat.common.portability.internal.PosixHelperImpl;
 import com.redhat.thermostat.common.portability.internal.UnimplementedError;
+import com.redhat.thermostat.common.utils.LoggingUtils;
 
 import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 public class MacOSProcessImpl implements PortableProcess {
 
+    private static final Logger LOGGER = LoggingUtils.getLogger(MacOSProcessImpl.class);
+    
     public static final MacOSProcessImpl INSTANCE = new MacOSProcessImpl();
     private static final MacOSHelperImpl helper = MacOSHelperImpl.INSTANCE;
     private PosixHelperImpl posixHelper = new PosixHelperImpl();
@@ -106,6 +111,16 @@
 
     @Override
     public String[] getNativeLibs(int pid) {
-        throw new UnimplementedError("getNativeLibs() is unsupported on macOS");
+            throw new UnimplementedError("getNativeLibs() is unsupported on macOS");    
+    }
+
+    @Override
+    public Long getResidentMemorySize(int pid) {
+        try {
+            throw new UnimplementedError("getResidentMemorySize()");    
+        } catch (UnimplementedError e) {
+            LOGGER.log(Level.WARNING, "getResidentMemorySize() is not available for MacOS at this moment");
+            return null;
+        }
     }
 }
--- a/common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/windows/WindowsPortableProcessImpl.java	Tue Sep 26 15:10:31 2017 +0200
+++ b/common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/windows/WindowsPortableProcessImpl.java	Wed Sep 20 12:23:54 2017 +0200
@@ -40,12 +40,18 @@
 import com.redhat.thermostat.common.portability.PortableProcess;
 import com.redhat.thermostat.common.portability.PortableProcessStat;
 import com.redhat.thermostat.common.portability.PortableVmIoStat;
+import com.redhat.thermostat.common.portability.internal.UnimplementedError;
+import com.redhat.thermostat.common.utils.LoggingUtils;
 
 import java.util.Collections;
 import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 public class WindowsPortableProcessImpl implements PortableProcess {
 
+    private static final Logger LOGGER = LoggingUtils.getLogger(WindowsPortableProcessImpl.class);
+    
     public static final WindowsPortableProcessImpl INSTANCE = new WindowsPortableProcessImpl();
     private static final WindowsHelperImpl helper = WindowsHelperImpl.INSTANCE;
 
@@ -111,4 +117,14 @@
     public String[] getNativeLibs(int pid) {
         return helper.getProcessModules(pid);
     }
+
+    @Override
+    public Long getResidentMemorySize(int pid) {
+        try {
+            throw new UnimplementedError("getResidentMemorySize()");    
+        } catch (UnimplementedError e) {
+            LOGGER.log(Level.WARNING, "getResidentMemorySize() is not available for Windows at this moment");
+            return null;
+        }
+    }
 }
--- a/common/portability/src/main/java/com/redhat/thermostat/common/portability/linux/ProcDataSource.java	Tue Sep 26 15:10:31 2017 +0200
+++ b/common/portability/src/main/java/com/redhat/thermostat/common/portability/linux/ProcDataSource.java	Wed Sep 20 12:23:54 2017 +0200
@@ -40,12 +40,11 @@
 import java.io.FileReader;
 import java.io.IOException;
 import java.io.Reader;
-
 /**
  * Wrapper for files under {@code /proc/}. See proc(5) for details about this.
  *
  * This class is inherently unportable, but a _lot_ of Linux code needs refactoring
- * before it can be make package private
+ * before it can be made package private
  *
  * Note that different Unix-like OSs may or may not have a /proc (and the format may be different)
  * for example Darwin/OSX doesn't have /proc
@@ -56,12 +55,15 @@
     private static final String STAT_FILE = "/proc/stat";
     private static final String MEMINFO_FILE = "/proc/meminfo";
     private static final String CPUINFO_FILE = "/proc/cpuinfo";
-
-    private static final String PID_ENVIRON_FILE = "/proc/${pid}/environ";
-    private static final String PID_IO_FILE = "/proc/${pid}/io";
-    private static final String PID_STAT_FILE = "/proc/${pid}/stat";
-    private static final String PID_STATUS_FILE = "/proc/${pid}/status";
-    private static final String PID_NUMA_MAPS_FILE = "/proc/${pid}/numa_maps";
+    
+    private static final String PROC_PREFIX = "/proc/";
+    private static final String PID_FORMAT = "%d";
+    private static final String PID_ENVIRON_FILE = getPidFileFormatter("/environ");
+    private static final String PID_IO_FILE = getPidFileFormatter("/io");
+    private static final String PID_STAT_FILE = getPidFileFormatter("/stat");
+    private static final String PID_STATUS_FILE = getPidFileFormatter("/status");
+    private static final String PID_NUMA_MAPS_FILE = getPidFileFormatter("/numa_maps");
+    private static final String PID_MAPS_FILE = getPidFileFormatter("/maps");
     
     private final ReaderCreator readerCreator;
     
@@ -74,6 +76,13 @@
     }
 
     /**
+     * @param file proc file beginning with a forward slash
+     */
+    private static String getPidFileFormatter(String file) {
+        return PROC_PREFIX + PID_FORMAT + file;
+    }
+    
+    /**
      * Returns a reader for /proc/cpuinfo
      */
     public Reader getCpuInfoReader() throws IOException {
@@ -135,17 +144,22 @@
     public Reader getNumaMapsReader(int pid) throws IOException {
         return readerCreator.createFileReader(getPidFile(PID_NUMA_MAPS_FILE, pid));
     }
+    
+    /**
+     * Returns a reader for /proc/$PID/maps
+     */
+    public Reader getMapsFile(int pid) throws IOException {
+        return readerCreator.createFileReader(getPidFile(PID_MAPS_FILE, pid));
+    }
 
-    private String getPidFile(String fileName, int pid) {
-        return fileName.replace("${pid}", Integer.toString(pid));
+    private String getPidFile(String fileNameFormatter, int pid) {
+        return String.format(fileNameFormatter, pid);
     }
     
-    // For testing purposes
     static class ReaderCreator {
         FileReader createFileReader(String fileName) throws FileNotFoundException {
             return new FileReader(fileName);
         }
     }
-
 }
 
--- a/common/portability/src/test/java/com/redhat/thermostat/common/portability/internal/linux/LinuxNativeLibsExtractorTest.java	Tue Sep 26 15:10:31 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,100 +0,0 @@
-/*
- * 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.linux;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-
-import java.io.File;
-import java.io.UnsupportedEncodingException;
-import java.net.URL;
-import java.net.URLDecoder;
-import java.util.Arrays;
-import java.util.HashSet;
-
-import org.junit.Test;
-
-public class LinuxNativeLibsExtractorTest {
-
-    private final File THREE_LIBS = getFileFromTestSources("native_lib_three_libs");
-    private final File NO_LIBS = getFileFromTestSources("native_lib_no_libs");
-    private final File EMPTY = getFileFromTestSources("native_lib_empty");
-
-    private File getFileFromTestSources(String path) {
-        path = '/' + path;
-        return new File(decodeFilePath(this.getClass().getResource(path)));
-    }
-
-    private String decodeFilePath(URL url) {
-        try {
-            return URLDecoder.decode(url.getFile(), "UTF-8");
-        } catch (UnsupportedEncodingException ex) {
-            throw new AssertionError(ex);
-        }
-    }
-
-    @Test
-    public void threeLibs() {
-        HashSet<String> expectedLibs = new HashSet<>(Arrays.asList("/usr/foo/libhello.so",
-                "/usr/bar/libworld.so.0.18.0", "/tmp/libnew.so.so"));
-
-        // Cannot compare arrays directly since the implementation internally gathers libs
-        // to a Set, which might yield a different ordering of items in the collection in the end
-        assertEquals(expectedLibs, new HashSet<>(Arrays.asList(LinuxNativeLibsExtractor.getNativeLibs(THREE_LIBS))));
-    }
-
-    @Test
-    public void noLibs() {
-        String[] result = LinuxNativeLibsExtractor.getNativeLibs(NO_LIBS);
-        int numExpectedLibs = 0;
-
-        assertNotNull(result);
-        assertEquals(result.length, numExpectedLibs);
-
-    }
-
-    @Test
-    public void empty() {
-        String[] result = LinuxNativeLibsExtractor.getNativeLibs(EMPTY);
-        int numExpectedLibs = 0;
-
-        assertNotNull(result);
-        assertEquals(result.length, numExpectedLibs);
-
-    }
-
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/portability/src/test/java/com/redhat/thermostat/common/portability/internal/linux/LinuxProcfsDataExtractorTest.java	Wed Sep 20 12:23:54 2017 +0200
@@ -0,0 +1,162 @@
+/*
+ * 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.linux;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import java.io.StringReader;
+import java.util.Arrays;
+import java.util.HashSet;
+
+import org.junit.Test;
+
+public class LinuxProcfsDataExtractorTest {
+
+    @Test
+    public void threeLibs() {
+        HashSet<String> expectedLibs = new HashSet<>(Arrays.asList("/usr/foo/libhello.so",
+                "/usr/bar/libworld.so.0.18.0", "/tmp/libnew.so.so"));
+        StringReader threeLibs = new StringReader(
+                "000000000000-00000000f000 r-xp 00000000 fd:01 1234567                    /usr/foo/libhello.so\n" +
+                "00000000f000-000000050000 ---p 00085000 fd:02 1234567                    /usr/bar/libworld.so.0.18.0\n" +
+                "000000050000-000000e00000 r--p 00085000 fd:03 1234567                    /tmp/libnew.so.so");
+        // Cannot compare arrays directly since the implementation internally gathers libs
+        // to a Set, which might yield a different ordering of items in the collection in the end
+        assertEquals(expectedLibs, new HashSet<>(Arrays.asList(LinuxProcfsDataExtractor.getNativeLibs(threeLibs))));
+    }
+
+    @Test
+    public void noLibs() {
+        StringReader noLibs = new StringReader(
+                "000000000000-00000000f000 r-xp 00000000 fd:01 1234567                    /usr/foo/libhello\n" +
+                "00000000f000-000000050000 ---p 00085000 fd:02 1234567                    /usr/bar/libworld\n" +
+                "000000050000-000000e00000 r--p 00085000 fd:03 1234567                    /tmp/libnew");
+        String[] result = LinuxProcfsDataExtractor.getNativeLibs(noLibs);
+        int numExpectedLibs = 0;
+
+        assertNotNull(result);
+        assertEquals(result.length, numExpectedLibs);
+
+    }
+
+    @Test
+    public void emptyFileNativeLibs() {
+        StringReader empty = new StringReader("");
+        String[] result = LinuxProcfsDataExtractor.getNativeLibs(empty);
+        int numExpectedLibs = 0;
+
+        assertNotNull(result);
+        assertEquals(result.length, numExpectedLibs);
+
+    }
+
+    @Test
+    public void emptyFileResidentMemory() {
+        StringReader empty = new StringReader("");
+        Long result = LinuxProcfsDataExtractor.getResidentMemorySize(empty);
+
+        assertNull(result);
+    }
+
+    @Test
+    public void missingResidentMemoryStat() {
+        StringReader resMemMissing = new StringReader(
+            "Uid:    1000    1000    1000    1000\n" +
+            "Gid:    100     100     100     100\n" +
+            "FDSize: 256\n" +
+            "Groups: 16 33 100\n" +
+            "NStgid: 17248\n" +
+            "NSpid:  17248\n" +
+            "NSpgid: 17248\n" +
+            "NSsid:  17200\n" +
+            "VmPeak:     131168 kB\n" +
+            "VmSize:     131168 kB\n" +
+            "VmLck:           0 kB\n" +
+            "VmPin:           0 kB\n" +
+            "VmHWM:       13484 kB\n" +
+            "RssAnon:     10264 kB\n" +
+            "RssFile:      3220 kB\n" +
+            "RssShmem:        0 kB\n" +
+            "VmData:      10332 kB\n" +
+            "VmStk:         136 kB\n" +
+            "VmExe:         992 kB\n" +
+            "VmLib:        2104 kB\n" +
+            "VmPTE:          76 kB\n" +
+            "VmPMD:          12 kB\n" +
+            "VmSwap:          0 kB");
+        Long result = LinuxProcfsDataExtractor.getResidentMemorySize(resMemMissing);
+
+        assertNull(result);
+    }
+
+    @Test
+    public void residentMemoryStatPresent() {
+        StringReader resMemPresent = new StringReader(
+            "Uid:    1000    1000    1000    1000\n" +
+            "Gid:    100     100     100     100\n" +
+            "FDSize: 256\n" +
+            "Groups: 16 33 100\n" +
+            "NStgid: 17248\n" +
+            "NSpid:  17248\n" +
+            "NSpgid: 17248\n" +
+            "NSsid:  17200\n" +
+            "VmPeak:     131168 kB\n" +
+            "VmSize:     131168 kB\n" +
+            "VmLck:           0 kB\n" +
+            "VmPin:           0 kB\n" +
+            "VmHWM:       13484 kB\n" +
+            "VmRSS:       13484 kB\n" +
+            "RssAnon:     10264 kB\n" +
+            "RssFile:      3220 kB\n" +
+            "RssShmem:        0 kB\n" +
+            "VmData:      10332 kB\n" +
+            "VmStk:         136 kB\n" +
+            "VmExe:         992 kB\n" +
+            "VmLib:        2104 kB\n" +
+            "VmPTE:          76 kB\n" +
+            "VmPMD:          12 kB\n" +
+            "VmSwap:          0 kB");
+        Long result = LinuxProcfsDataExtractor.getResidentMemorySize(resMemPresent);
+
+        long expected = 13484;
+
+        assertEquals(result.longValue(), expected);
+    }
+
+}
--- a/common/portability/src/test/resources/native_lib_empty	Tue Sep 26 15:10:31 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-
--- a/common/portability/src/test/resources/native_lib_no_libs	Tue Sep 26 15:10:31 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,3 +0,0 @@
-000000000000-00000000f000 r-xp 00000000 fd:01 1234567                    /usr/foo/libhello
-00000000f000-000000050000 ---p 00085000 fd:02 1234567                    /usr/bar/libworld
-000000050000-000000e00000 r--p 00085000 fd:03 1234567                    /tmp/libnew
--- a/common/portability/src/test/resources/native_lib_three_libs	Tue Sep 26 15:10:31 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,4 +0,0 @@
-000000000000-00000000f000 r-xp 00000000 fd:01 1234567                    /usr/foo/libhello.so
-00000000f000-000000050000 ---p 00085000 fd:02 1234567                    /usr/bar/libworld.so.0.18.0
-000000050000-000000e00000 r--p 00085000 fd:03 1234567                    /tmp/libnew.so.so
-
--- a/plugins/jvm-overview/agent/src/main/java/com/redhat/thermostat/jvm/overview/agent/internal/model/VmNativeLibsExtractorFactory.java	Tue Sep 26 15:10:31 2017 +0200
+++ b/plugins/jvm-overview/agent/src/main/java/com/redhat/thermostat/jvm/overview/agent/internal/model/VmNativeLibsExtractorFactory.java	Wed Sep 20 12:23:54 2017 +0200
@@ -36,8 +36,6 @@
 
 package com.redhat.thermostat.jvm.overview.agent.internal.model;
 
-import com.redhat.thermostat.shared.config.OS;
-
 public final class VmNativeLibsExtractorFactory {
 
     public static VmNativeLibsExtractor getInstance(Integer pid) {
--- a/plugins/vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryBackend.java	Tue Sep 26 15:10:31 2017 +0200
+++ b/plugins/vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryBackend.java	Wed Sep 20 12:23:54 2017 +0200
@@ -55,7 +55,7 @@
 @Component
 @Service(value = Backend.class)
 public class VmMemoryBackend extends VmListenerBackend {
-    
+
     private final ListenerCreator listenerCreator;
 
     @Reference
@@ -107,12 +107,12 @@
 
     @Override
     protected VmUpdateListener createVmListener(String writerId, String vmId, int pid) {
-        return listenerCreator.create(writerId, vmMemoryStatDAO, vmTlabStatDAO, vmId);
+        return listenerCreator.create(writerId, vmMemoryStatDAO, vmTlabStatDAO, vmId, pid);
     }
 
     static class ListenerCreator {
-        VmMemoryVmListener create(String writerId, VmMemoryStatDAO dao, VmTlabStatDAO tlabDao, String vmId) {
-            return new VmMemoryVmListener(writerId, dao, tlabDao, vmId);
+        VmMemoryVmListener create(String writerId, VmMemoryStatDAO dao, VmTlabStatDAO tlabDao, String vmId, int pid) {
+            return new VmMemoryVmListener(writerId, dao, tlabDao, vmId, pid);
         }
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryResidentSizeExtractor.java	Wed Sep 20 12:23:54 2017 +0200
@@ -0,0 +1,43 @@
+/*
+ * 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.vm.memory.agent.internal;
+
+
+public interface VmMemoryResidentSizeExtractor {
+
+    Long getResidentMemorySize(int pid);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryResidentSizeExtractorImpl.java	Wed Sep 20 12:23:54 2017 +0200
@@ -0,0 +1,66 @@
+/*
+ * 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.vm.memory.agent.internal;
+
+import com.redhat.thermostat.common.portability.PortableProcess;
+import com.redhat.thermostat.common.portability.PortableProcessFactory;
+import com.redhat.thermostat.vm.memory.agent.model.VmMemoryStat;
+
+public class VmMemoryResidentSizeExtractorImpl implements VmMemoryResidentSizeExtractor {
+
+    private final PortableProcess helper;
+    private static final VmMemoryResidentSizeExtractor INSTANCE = new VmMemoryResidentSizeExtractorImpl();
+
+    public static VmMemoryResidentSizeExtractor getInstance() {
+        return INSTANCE;
+    }
+
+    public VmMemoryResidentSizeExtractorImpl() {
+        helper = PortableProcessFactory.getInstance();
+    }
+
+    VmMemoryResidentSizeExtractorImpl(PortableProcess helper) {
+        this.helper = helper;
+    }
+
+    @Override
+    public Long getResidentMemorySize(int pid) {
+        Long result = helper.getResidentMemorySize(pid);
+        return result == null ? VmMemoryStat.UNKNOWN : result;
+    }
+
+}
--- a/plugins/vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryVmListener.java	Tue Sep 26 15:10:31 2017 +0200
+++ b/plugins/vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryVmListener.java	Wed Sep 20 12:23:54 2017 +0200
@@ -55,27 +55,29 @@
 import com.redhat.thermostat.vm.memory.agent.model.VmTlabStat;
 
 public class VmMemoryVmListener implements VmUpdateListener {
-    
+
     private static final Logger logger = LoggingUtils.getLogger(VmMemoryVmListener.class);
 
     private final String vmId;
+    private final int pid;
     private final VmMemoryStatDAO memDAO;
     private final VmTlabStatDAO tlabDAO;
     private final String writerId;
     private final Clock clock;
-    
+
     private boolean error;
 
-    public VmMemoryVmListener(String writerId, VmMemoryStatDAO vmMemoryStatDao, VmTlabStatDAO vmTlabStatDao, String vmId) {
-        this(writerId, vmMemoryStatDao, vmTlabStatDao, new SystemClock(), vmId);
+    public VmMemoryVmListener(String writerId, VmMemoryStatDAO vmMemoryStatDao, VmTlabStatDAO vmTlabStatDao, String vmId, int pid) {
+        this(writerId, vmMemoryStatDao, vmTlabStatDao, new SystemClock(), vmId, pid);
     }
 
-    public VmMemoryVmListener(String writerId, VmMemoryStatDAO vmMemoryStatDao, VmTlabStatDAO vmTlabStatDao, Clock clock, String vmId) {
+    public VmMemoryVmListener(String writerId, VmMemoryStatDAO vmMemoryStatDao, VmTlabStatDAO vmTlabStatDao, Clock clock, String vmId, int pid) {
         this.memDAO = vmMemoryStatDao;
         this.tlabDAO = vmTlabStatDao;
         this.clock = clock;
         this.vmId = vmId;
         this.writerId = writerId;
+        this.pid = pid;
     }
 
     @Override
@@ -85,10 +87,19 @@
         recordTlabStat(extractor);
     }
 
+    void countersUpdated(VmUpdate update, VmMemoryResidentSizeExtractor residentMemoryExtractor) {
+        VmMemoryDataExtractor extractor = new VmMemoryDataExtractor(update);
+        recordMemoryStat(extractor, residentMemoryExtractor);
+        recordTlabStat(extractor);
+    }
+
     void recordMemoryStat(VmMemoryDataExtractor extractor) {
+        recordMemoryStat(extractor, VmMemoryResidentSizeExtractorImpl.getInstance());
+    }
+
+    void recordMemoryStat(VmMemoryDataExtractor extractor, VmMemoryResidentSizeExtractor residentSizeExtractor) {
         try {
             long timestamp = clock.getRealTimeMillis();
-
             long metaspaceMaxCapacity = extractor.getMetaspaceMaxCapacity(VmMemoryStat.UNKNOWN);
             long metaspaceMinCapacity = extractor.getMetaspaceMinCapacity(VmMemoryStat.UNKNOWN);
             long metaspaceCapacity = extractor.getMetaspaceCapacity(VmMemoryStat.UNKNOWN);
@@ -119,9 +130,11 @@
                         }
                     }
                 }
-                VmMemoryStat stat = new VmMemoryStat(writerId, timestamp, vmId, 
+                Long residentMemory = residentSizeExtractor.getResidentMemorySize(pid);
+                VmMemoryStat stat = new VmMemoryStat(writerId, timestamp, vmId,
                         generations.toArray(new Generation[generations.size()]),
-                        metaspaceMaxCapacity, metaspaceMinCapacity, metaspaceCapacity, metaspaceUsed);
+                        metaspaceMaxCapacity, metaspaceMinCapacity, metaspaceCapacity,
+                        metaspaceUsed, residentMemory);
                 memDAO.putVmMemoryStat(stat);
             }
             else {
@@ -136,29 +149,29 @@
             int generation) throws VmUpdateException {
         String name = extractor.getGenerationName(generation);
         if (name == null) {
-            logWarningOnce("Unable to determine name of generation " 
+            logWarningOnce("Unable to determine name of generation "
                     + generation + " for VM " + vmId);
             return null;
         }
         Long capacity = extractor.getGenerationCapacity(generation);
         if (capacity == null) {
-            logWarningOnce("Unable to determine capacity of generation " 
+            logWarningOnce("Unable to determine capacity of generation "
                     + generation + " for VM " + vmId);
             return null;
         }
         Long maxCapacity = extractor.getGenerationMaxCapacity(generation);
         if (maxCapacity == null) {
-            logWarningOnce("Unable to determine max capacity of generation " 
+            logWarningOnce("Unable to determine max capacity of generation "
                     + generation + " for VM " + vmId);
             return null;
         }
         String collector = extractor.getGenerationCollector(generation);
         if (collector == null) {
-            logWarningOnce("Unable to determine collector of generation " 
+            logWarningOnce("Unable to determine collector of generation "
                     + generation + " for VM " + vmId);
             return null;
         }
-        
+
         Generation g = new Generation();
         g.setName(name);
         g.setCapacity(capacity);
@@ -171,29 +184,29 @@
             int space) throws VmUpdateException {
         String name = extractor.getSpaceName(generation, space);
         if (name == null) {
-            logWarningOnce("Unable to determine name of space " + space 
+            logWarningOnce("Unable to determine name of space " + space
                     + " in generation " + generation + " for VM " + vmId);
             return null;
         }
         Long capacity = extractor.getSpaceCapacity(generation, space);
         if (capacity == null) {
-            logWarningOnce("Unable to determine capacity of space " + space 
+            logWarningOnce("Unable to determine capacity of space " + space
                     + " in generation " + generation + " for VM " + vmId);
             return null;
         }
         Long maxCapacity = extractor.getSpaceMaxCapacity(generation, space);
         if (maxCapacity == null) {
-            logWarningOnce("Unable to determine max capacity of space " + space 
+            logWarningOnce("Unable to determine max capacity of space " + space
                     + " in generation " + generation + " for VM " + vmId);
             return null;
         }
         Long used = extractor.getSpaceUsed(generation, space);
         if (used == null) {
-            logWarningOnce("Unable to determine used memory of space " + space 
+            logWarningOnce("Unable to determine used memory of space " + space
                     + " in generation " + generation + " for VM " + vmId);
             return null;
         }
-        
+
         Space s = new Space();
         s.setIndex(space);
         s.setName(name);
--- a/plugins/vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/model/VmMemoryStat.java	Tue Sep 26 15:10:31 2017 +0200
+++ b/plugins/vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/model/VmMemoryStat.java	Wed Sep 20 12:23:54 2017 +0200
@@ -186,13 +186,15 @@
     private long metaspaceMinCapacity;
     private long metaspaceCapacity;
     private long metaspaceUsed;
+    private long residentMemory;
 
     public VmMemoryStat() {
-        this(null, UNKNOWN, null, null, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN);
+        this(null, UNKNOWN, null, null, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN);
     }
 
     public VmMemoryStat(String writerId, long timestamp, String jvmId, Generation[] generations,
-                        long metaspaceMaxCapacity, long metaspaceMinCapacity, long metaspaceCapacity, long metaspaceUsed) {
+                        long metaspaceMaxCapacity, long metaspaceMinCapacity,
+                        long metaspaceCapacity, long metaspaceUsed, long residentMemory) {
         super(writerId);
         this.timeStamp = timestamp;
         this.jvmId = jvmId;
@@ -203,6 +205,7 @@
         this.metaspaceMinCapacity = metaspaceMinCapacity;
         this.metaspaceCapacity = metaspaceCapacity;
         this.metaspaceUsed = metaspaceUsed;
+        this.residentMemory = residentMemory;
         checkSaneValuesForMetaspace();
     }
 
@@ -286,6 +289,16 @@
         this.metaspaceUsed = used;
     }
 
+    @Persist
+    public long getResidentMemory() {
+        return residentMemory;
+    }
+
+    @Persist
+    public void setResidentMemory(long residentMemory) {
+        this.residentMemory = residentMemory;
+    }
+
     public boolean isMetaspacePresent() {
         checkSaneValuesForMetaspace();
         if (metaspaceCapacity == UNKNOWN) {
--- a/plugins/vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryBackendTest.java	Tue Sep 26 15:10:31 2017 +0200
+++ b/plugins/vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryBackendTest.java	Wed Sep 20 12:23:54 2017 +0200
@@ -123,7 +123,7 @@
         backend.bindVmTlabStatDAO(tlabDao);
         backend.createVmListener(writerId, vmId, pid);
 
-        verify(listenerCreator).create(writerId, dao, tlabDao, vmId);
+        verify(listenerCreator).create(writerId, dao, tlabDao, vmId, pid);
     }
 
     static class TestVmMemoryBackend extends VmMemoryBackend {
--- a/plugins/vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryVmListenerTest.java	Tue Sep 26 15:10:31 2017 +0200
+++ b/plugins/vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryVmListenerTest.java	Wed Sep 20 12:23:54 2017 +0200
@@ -65,7 +65,7 @@
     private static final Long[] GEN_MAX_CAPS = new Long[] { 5000L, 10000L };
     private static final String[] GEN_GCS = new String[] { "GC1", "GC2" };
     private static final Long[] GEN_SPACES = new Long[] { 2L, 1L };
-    private static final String[][] SPACE_NAME = new String[][] { 
+    private static final String[][] SPACE_NAME = new String[][] {
         { "Space1", "Space2" },
         { "Space3" }
     };
@@ -86,19 +86,29 @@
     private static final long METASPACE_MIN_CAPACITY = 0;
     private static final long METASPACE_CAPACITY = 10;
     private static final long METASPACE_USED = 5;
-    
+    private static final long RESIDENT_MEMORY_SIZE = 1024L;
+
     private VmMemoryVmListener vmListener;
     private VmMemoryDataExtractor extractor;
     private VmMemoryStatDAO vmMemoryStatDAO;
     private VmTlabStatDAO vmTlabStatDAO;
-    
+    private VmMemoryResidentSizeExtractor vmResidentSizeExtractor;
+
     @Before
     public void setup() throws VmUpdateException {
         final int numGens = 2;
         vmMemoryStatDAO = mock(VmMemoryStatDAO.class);
         vmTlabStatDAO = mock(VmTlabStatDAO.class);
-        vmListener = new VmMemoryVmListener("foo-agent", vmMemoryStatDAO, vmTlabStatDAO, "jvmId");
+        //We can use arbitrary pid as this will not be used 
+        //in the used VmMemoryResidentSizeExtractor anyway
+        vmListener = new VmMemoryVmListener("foo-agent", vmMemoryStatDAO, vmTlabStatDAO, "jvmId", 0);
         extractor = mock(VmMemoryDataExtractor.class);
+        vmResidentSizeExtractor = new VmMemoryResidentSizeExtractor() {
+            @Override
+            public Long getResidentMemorySize(int pid) {
+                return RESIDENT_MEMORY_SIZE;
+            }
+        };
 
         mockTotalGenerations(numGens);
 
@@ -128,7 +138,7 @@
     private void mockGenerationName(int gen) throws VmUpdateException {
         when(extractor.getGenerationName(gen)).thenReturn(GEN_NAMES[gen]);
     }
-    
+
     private void mockGenerationCapacity(int gen) throws VmUpdateException {
         when(extractor.getGenerationCapacity(gen)).thenReturn(GEN_CAPS[gen]);
     }
@@ -136,27 +146,27 @@
     private void mockGenerationMaxCapacity(int gen) throws VmUpdateException {
         when(extractor.getGenerationMaxCapacity(gen)).thenReturn(GEN_MAX_CAPS[gen]);
     }
-    
+
     private void mockGenerationGC(int gen) throws VmUpdateException {
         when(extractor.getGenerationCollector(gen)).thenReturn(GEN_GCS[gen]);
     }
-    
+
     private void mockTotalSpaces(int gen) throws VmUpdateException {
         when(extractor.getTotalSpaces(gen)).thenReturn(GEN_SPACES[gen]);
     }
-    
+
     private void mockSpaceName(int gen, int space) throws VmUpdateException {
         when(extractor.getSpaceName(gen, space)).thenReturn(SPACE_NAME[gen][space]);
     }
-    
+
     private void mockSpaceCapacity(int gen, int space) throws VmUpdateException {
         when(extractor.getSpaceCapacity(gen, space)).thenReturn(SPACE_CAPS[gen][space]);
     }
-    
+
     private void mockSpaceMaxCapacity(int gen, int space) throws VmUpdateException {
         when(extractor.getSpaceMaxCapacity(gen, space)).thenReturn(SPACE_MAX_CAPS[gen][space]);
     }
-    
+
     private void mockSpaceUsed(int gen, int space) throws VmUpdateException {
         when(extractor.getSpaceUsed(gen, space)).thenReturn(SPACE_USED[gen][space]);
     }
@@ -175,7 +185,7 @@
     @Test
     public void testMonitorsUpdated() throws VmUpdateException {
         VmUpdate update = mock(VmUpdate.class);
-        vmListener.countersUpdated(update);
+        vmListener.countersUpdated(update, vmResidentSizeExtractor);
 
         verify(vmMemoryStatDAO).putVmMemoryStat(isA(VmMemoryStat.class));
         verify(vmTlabStatDAO).putStat(isA(VmTlabStat.class));
@@ -183,11 +193,11 @@
 
     @Test
     public void testRecordMemoryStat() {
-        vmListener.recordMemoryStat(extractor);
+        vmListener.recordMemoryStat(extractor, vmResidentSizeExtractor);
         ArgumentCaptor<VmMemoryStat> captor = ArgumentCaptor.forClass(VmMemoryStat.class);
         verify(vmMemoryStatDAO).putVmMemoryStat(captor.capture());
         VmMemoryStat memoryStat = captor.getValue();
-        
+
         Generation[] gens = memoryStat.getGenerations();
         assertEquals(2, gens.length);
         for (int i = 0; i < gens.length; i++) {
@@ -206,7 +216,7 @@
                 assertEquals(SPACE_USED[i][j], (Long) space.getUsed());
             }
         }
-
+        assertEquals(RESIDENT_MEMORY_SIZE, memoryStat.getResidentMemory());
         assertEquals(METASPACE_MAX_CAPACITY, memoryStat.getMetaspaceMaxCapacity());
         assertEquals(METASPACE_MIN_CAPACITY, memoryStat.getMetaspaceMinCapacity());
         assertEquals(METASPACE_CAPACITY, memoryStat.getMetaspaceCapacity());
@@ -216,26 +226,26 @@
     @Test
     public void testRecordingMemoryInPresenseOfExtrationErrors() throws VmUpdateException {
         when(extractor.getTotalGcGenerations()).thenThrow(new VmUpdateException());
-        vmListener.recordMemoryStat(extractor);
+        vmListener.recordMemoryStat(extractor, vmResidentSizeExtractor);
 
         verifyNoMoreInteractions(vmMemoryStatDAO);
     }
-    
+
     @Test
     public void testRecordMemoryStatNoTotal() throws VmUpdateException {
         when(extractor.getTotalGcGenerations()).thenReturn(null);
-        vmListener.recordMemoryStat(extractor);
+        vmListener.recordMemoryStat(extractor, vmResidentSizeExtractor);
         verify(vmMemoryStatDAO, never()).putVmMemoryStat(any(VmMemoryStat.class));
     }
 
     @Test
     public void testRecordMemoryStatNoName() throws VmUpdateException {
         when(extractor.getGenerationName(0)).thenReturn(null);
-        vmListener.recordMemoryStat(extractor);
+        vmListener.recordMemoryStat(extractor, vmResidentSizeExtractor);
         ArgumentCaptor<VmMemoryStat> captor = ArgumentCaptor.forClass(VmMemoryStat.class);
         verify(vmMemoryStatDAO).putVmMemoryStat(captor.capture());
         VmMemoryStat memoryStat = captor.getValue();
-        
+
         Generation[] gens = memoryStat.getGenerations();
         assertEquals(1, gens.length);
         Generation gen = gens[0];
@@ -253,15 +263,15 @@
             assertEquals(SPACE_USED[1][j], (Long) space.getUsed());
         }
     }
-    
+
     @Test
     public void testRecordMemoryStatNoCapacity() throws VmUpdateException {
         when(extractor.getGenerationCapacity(0)).thenReturn(null);
-        vmListener.recordMemoryStat(extractor);
+        vmListener.recordMemoryStat(extractor, vmResidentSizeExtractor);
         ArgumentCaptor<VmMemoryStat> captor = ArgumentCaptor.forClass(VmMemoryStat.class);
         verify(vmMemoryStatDAO).putVmMemoryStat(captor.capture());
         VmMemoryStat memoryStat = captor.getValue();
-        
+
         Generation[] gens = memoryStat.getGenerations();
         assertEquals(1, gens.length);
         Generation gen = gens[0];
@@ -279,15 +289,15 @@
             assertEquals(SPACE_USED[1][j], (Long) space.getUsed());
         }
     }
-    
+
     @Test
     public void testRecordMemoryStatNoMaxCapacity() throws VmUpdateException {
         when(extractor.getGenerationMaxCapacity(0)).thenReturn(null);
-        vmListener.recordMemoryStat(extractor);
+        vmListener.recordMemoryStat(extractor, vmResidentSizeExtractor);
         ArgumentCaptor<VmMemoryStat> captor = ArgumentCaptor.forClass(VmMemoryStat.class);
         verify(vmMemoryStatDAO).putVmMemoryStat(captor.capture());
         VmMemoryStat memoryStat = captor.getValue();
-        
+
         Generation[] gens = memoryStat.getGenerations();
         assertEquals(1, gens.length);
         Generation gen = gens[0];
@@ -305,15 +315,15 @@
             assertEquals(SPACE_USED[1][j], (Long) space.getUsed());
         }
     }
-    
+
     @Test
     public void testRecordMemoryStatNoCollector() throws VmUpdateException {
         when(extractor.getGenerationCollector(0)).thenReturn(null);
-        vmListener.recordMemoryStat(extractor);
+        vmListener.recordMemoryStat(extractor,vmResidentSizeExtractor);
         ArgumentCaptor<VmMemoryStat> captor = ArgumentCaptor.forClass(VmMemoryStat.class);
         verify(vmMemoryStatDAO).putVmMemoryStat(captor.capture());
         VmMemoryStat memoryStat = captor.getValue();
-        
+
         Generation[] gens = memoryStat.getGenerations();
         assertEquals(1, gens.length);
         Generation gen = gens[0];
@@ -331,15 +341,15 @@
             assertEquals(SPACE_USED[1][j], (Long) space.getUsed());
         }
     }
-    
+
     @Test
     public void testRecordMemoryStatNoTotalSpaces() throws VmUpdateException {
         when(extractor.getTotalSpaces(0)).thenReturn(null);
-        vmListener.recordMemoryStat(extractor);
+        vmListener.recordMemoryStat(extractor, vmResidentSizeExtractor);
         ArgumentCaptor<VmMemoryStat> captor = ArgumentCaptor.forClass(VmMemoryStat.class);
         verify(vmMemoryStatDAO).putVmMemoryStat(captor.capture());
         VmMemoryStat memoryStat = captor.getValue();
-        
+
         Generation[] gens = memoryStat.getGenerations();
         assertEquals(1, gens.length);
         Generation gen = gens[0];
@@ -357,15 +367,15 @@
             assertEquals(SPACE_USED[1][j], (Long) space.getUsed());
         }
     }
-    
+
     @Test
     public void testRecordMemoryStatNoSpaceName() throws VmUpdateException {
         when(extractor.getSpaceName(0, 1)).thenReturn(null);
-        vmListener.recordMemoryStat(extractor);
+        vmListener.recordMemoryStat(extractor, vmResidentSizeExtractor);
         ArgumentCaptor<VmMemoryStat> captor = ArgumentCaptor.forClass(VmMemoryStat.class);
         verify(vmMemoryStatDAO).putVmMemoryStat(captor.capture());
         VmMemoryStat memoryStat = captor.getValue();
-        
+
         Generation[] gens = memoryStat.getGenerations();
         assertEquals(2, gens.length);
         for (int i = 0; i < gens.length; i++) {
@@ -391,15 +401,15 @@
             }
         }
     }
-    
+
     @Test
     public void testRecordMemoryStatNoSpaceCapacity() throws VmUpdateException {
         when(extractor.getSpaceCapacity(0, 1)).thenReturn(null);
-        vmListener.recordMemoryStat(extractor);
+        vmListener.recordMemoryStat(extractor, vmResidentSizeExtractor);
         ArgumentCaptor<VmMemoryStat> captor = ArgumentCaptor.forClass(VmMemoryStat.class);
         verify(vmMemoryStatDAO).putVmMemoryStat(captor.capture());
         VmMemoryStat memoryStat = captor.getValue();
-        
+
         Generation[] gens = memoryStat.getGenerations();
         assertEquals(2, gens.length);
         for (int i = 0; i < gens.length; i++) {
@@ -425,15 +435,15 @@
             }
         }
     }
-    
+
     @Test
     public void testRecordMemoryStatNoSpaceMaxCapacity() throws VmUpdateException {
         when(extractor.getSpaceMaxCapacity(0, 1)).thenReturn(null);
-        vmListener.recordMemoryStat(extractor);
+        vmListener.recordMemoryStat(extractor, vmResidentSizeExtractor);
         ArgumentCaptor<VmMemoryStat> captor = ArgumentCaptor.forClass(VmMemoryStat.class);
         verify(vmMemoryStatDAO).putVmMemoryStat(captor.capture());
         VmMemoryStat memoryStat = captor.getValue();
-        
+
         Generation[] gens = memoryStat.getGenerations();
         assertEquals(2, gens.length);
         for (int i = 0; i < gens.length; i++) {
@@ -459,15 +469,15 @@
             }
         }
     }
-    
+
     @Test
     public void testRecordMemoryStatNoSpaceUsed() throws VmUpdateException {
         when(extractor.getSpaceUsed(0, 1)).thenReturn(null);
-        vmListener.recordMemoryStat(extractor);
+        vmListener.recordMemoryStat(extractor, vmResidentSizeExtractor);
         ArgumentCaptor<VmMemoryStat> captor = ArgumentCaptor.forClass(VmMemoryStat.class);
         verify(vmMemoryStatDAO).putVmMemoryStat(captor.capture());
         VmMemoryStat memoryStat = captor.getValue();
-        
+
         Generation[] gens = memoryStat.getGenerations();
         assertEquals(2, gens.length);
         for (int i = 0; i < gens.length; i++) {
--- a/plugins/vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/models/VmMemoryStatDAOImplTest.java	Tue Sep 26 15:10:31 2017 +0200
+++ b/plugins/vm-memory/agent/src/test/java/com/redhat/thermostat/vm/memory/agent/internal/models/VmMemoryStatDAOImplTest.java	Wed Sep 20 12:23:54 2017 +0200
@@ -62,7 +62,7 @@
 
     private static final String JSON = "{\"this\":\"is\",\"test\":\"JSON\"}";
     private static final URI GATEWAY_URI = URI.create("http://example.com/jvm-memory/0.0.2/");
-    
+
     private JsonHelper jsonHelper;
     private PluginConfiguration config;
     VmMemoryStatDAOImpl.ConfigurationCreator creator;
@@ -73,7 +73,7 @@
     public void setUp() throws Exception {
         jsonHelper = mock(JsonHelper.class);
         when(jsonHelper.toJson(anyListOf(VmMemoryStat.class))).thenReturn(JSON);
-        
+
         config = mock(PluginConfiguration.class);
         when(config.getGatewayURL()).thenReturn(GATEWAY_URI);
 
@@ -115,7 +115,7 @@
             gen.setSpaces(spaces.toArray(new Space[spaces.size()]));
         }
         VmMemoryStat stat = new VmMemoryStat("foo-agent", 1, "jvmId", generations.toArray(new Generation[generations.size()]),
-                2, 3, 4, 5);
+                2, 3, 4, 5, 0);
 
         VmMemoryStatDAOImpl dao = new VmMemoryStatDAOImpl(jsonHelper, creator, source);
         dao.bindHttpRequestService(httpRequestService);
@@ -123,12 +123,12 @@
         when(id.getSystemID()).thenReturn("systemid");
         dao.bindSystemID(id);
         dao.activate();
-        
+
         dao.putVmMemoryStat(stat);
 
         verify(jsonHelper).toJson(Arrays.asList(stat));
         verify(httpRequestService).sendHttpRequest(JSON, GATEWAY_URI.resolve("systems/systemid/jvms/jvmId"), HttpRequestService.Method.POST);
     }
-    
+
 }