Project import
diff --git a/libpagemap/Android.bp b/libpagemap/Android.bp
new file mode 100644
index 0000000..e8cc4af
--- /dev/null
+++ b/libpagemap/Android.bp
@@ -0,0 +1,32 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_shared {
+    name: "libpagemap",
+    srcs: [
+        "pm_kernel.c",
+        "pm_process.c",
+        "pm_map.c",
+        "pm_memusage.c",
+    ],
+    local_include_dirs: ["include"],
+    cflags: ["-Wno-unused-parameter"],
+    export_include_dirs: ["include"],
+}
+
+cc_test {
+    name: "pagemap_test",
+    srcs: ["pagemap_test.cpp"],
+    shared_libs: ["libpagemap"],
+}
diff --git a/libpagemap/MODULE_LICENSE_APACHE2 b/libpagemap/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libpagemap/MODULE_LICENSE_APACHE2
diff --git a/libpagemap/NOTICE b/libpagemap/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/libpagemap/NOTICE
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2005-2008, The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/libpagemap/include/pagemap/pagemap.h b/libpagemap/include/pagemap/pagemap.h
new file mode 100644
index 0000000..61e59e2
--- /dev/null
+++ b/libpagemap/include/pagemap/pagemap.h
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _PAGEMAP_PAGEMAP_H
+#define _PAGEMAP_PAGEMAP_H
+
+#include <stdint.h>
+#include <stdio.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <sys/queue.h>
+
+__BEGIN_DECLS
+
+typedef struct pm_proportional_swap pm_proportional_swap_t;
+
+typedef struct pm_swap_offset pm_swap_offset_t;
+
+struct pm_swap_offset {
+    unsigned int offset;
+    SIMPLEQ_ENTRY(pm_swap_offset) simpleqe;
+};
+
+typedef struct pm_memusage pm_memusage_t;
+
+/* Holds the various metrics for memory usage of a process or a mapping. */
+struct pm_memusage {
+    size_t vss;
+    size_t rss;
+    size_t pss;
+    size_t uss;
+    size_t swap;
+    /* if non NULL then use swap_offset_list to compute proportional swap */
+    pm_proportional_swap_t *p_swap;
+    SIMPLEQ_HEAD(simpleqhead, pm_swap_offset) swap_offset_list;
+};
+
+typedef struct pm_swapusage pm_swapusage_t;
+struct pm_swapusage {
+    size_t proportional;
+    size_t unique;
+};
+
+/* Clears a memusage. */
+void pm_memusage_zero(pm_memusage_t *mu);
+/* Adds one memusage (a) to another (b). */
+void pm_memusage_add(pm_memusage_t *a, pm_memusage_t *b);
+/* Adds a swap offset */
+void pm_memusage_pswap_add_offset(pm_memusage_t *mu, unsigned int offset);
+/* Enable proportional swap computing. */
+void pm_memusage_pswap_init_handle(pm_memusage_t *mu, pm_proportional_swap_t *p_swap);
+/* Computes and return the proportional swap */
+void pm_memusage_pswap_get_usage(pm_memusage_t *mu, pm_swapusage_t *su);
+void pm_memusage_pswap_free(pm_memusage_t *mu);
+/* Initialize a proportional swap computing handle:
+   assumes only 1 swap device, total swap size of this device in bytes to be given as argument */
+pm_proportional_swap_t * pm_memusage_pswap_create(int swap_size);
+void pm_memusage_pswap_destroy(pm_proportional_swap_t *p_swap);
+
+typedef struct pm_kernel   pm_kernel_t;
+typedef struct pm_process  pm_process_t;
+typedef struct pm_map      pm_map_t;
+
+/* pm_kernel_t holds the state necessary to interface to the kernel's pagemap
+ * system on a global level. */
+struct pm_kernel {
+    int kpagecount_fd;
+    int kpageflags_fd;
+
+    int pagesize;
+};
+
+/* pm_process_t holds the state necessary to interface to a particular process'
+ * pagemap. */
+struct pm_process {
+    pm_kernel_t *ker;
+
+    pid_t pid;
+
+    pm_map_t **maps;
+    int num_maps;
+
+    int pagemap_fd;
+};
+
+/* pm_map_t holds the state necessary to access information about a particular
+ * mapping in a particular process. */
+struct pm_map {
+    pm_process_t *proc;
+
+    uint64_t start;
+    uint64_t end;
+    uint64_t offset;
+    int flags;
+
+    char *name;
+};
+
+/* Create a pm_kernel_t. */
+int pm_kernel_create(pm_kernel_t **ker_out);
+
+#define pm_kernel_pagesize(ker) ((ker)->pagesize)
+
+/* Get a list of probably-existing PIDs (returned through *pids_out).
+ * Length of the array (in sizeof(pid_t) units) is returned through *len.
+ * The array should be freed by the caller. */
+int pm_kernel_pids(pm_kernel_t *ker, pid_t **pids_out, size_t *len);
+
+/* Get the map count (from /proc/kpagecount) of a physical frame.
+ * The count is returned through *count_out. */
+int pm_kernel_count(pm_kernel_t *ker, uint64_t pfn, uint64_t *count_out);
+
+/* Get the page flags (from /proc/kpageflags) of a physical frame.
+ * The count is returned through *flags_out. */
+int pm_kernel_flags(pm_kernel_t *ker, uint64_t pfn, uint64_t *flags_out);
+
+#define PM_PAGE_LOCKED     (1 <<  0)
+#define PM_PAGE_ERROR      (1 <<  1)
+#define PM_PAGE_REFERENCED (1 <<  2)
+#define PM_PAGE_UPTODATE   (1 <<  3)
+#define PM_PAGE_DIRTY      (1 <<  4)
+#define PM_PAGE_LRU        (1 <<  5)
+#define PM_PAGE_ACTIVE     (1 <<  6)
+#define PM_PAGE_SLAB       (1 <<  7)
+#define PM_PAGE_WRITEBACK  (1 <<  8)
+#define PM_PAGE_RECLAIM    (1 <<  9)
+#define PM_PAGE_BUDDY      (1 << 10)
+
+/* for kernels >= 2.6.31 */
+#define PM_PAGE_MMAP          (1 << 11)
+#define PM_PAGE_ANON          (1 << 12)
+#define PM_PAGE_SWAPCACHE     (1 << 13)
+#define PM_PAGE_SWAPBACKED    (1 << 14)
+#define PM_PAGE_COMPOUND_HEAD (1 << 15)
+#define PM_PAGE_COMPOUND_TAIL (1 << 16)
+#define PM_PAGE_HUGE          (1 << 17)
+#define PM_PAGE_UNEVICTABLE   (1 << 18)
+#define PM_PAGE_HWPOISON      (1 << 19)
+#define PM_PAGE_NOPAGE        (1 << 20)
+
+/* for kernels >= 2.6.32 */
+#define PM_PAGE_KSM           (1 << 21)
+
+/* for kernels >= 3.4 */
+#define PM_PAGE_THP           (1 << 22)
+
+/* Destroy a pm_kernel_t. */
+int pm_kernel_destroy(pm_kernel_t *ker);
+
+/* Get the PID of a pm_process_t. */
+#define pm_process_pid(proc) ((proc)->pid)
+
+/* Create a pm_process_t and returns it through *proc_out.
+ * Takes a pm_kernel_t, and the PID of the process. */
+int pm_process_create(pm_kernel_t *ker, pid_t pid, pm_process_t **proc_out);
+
+/* Get the total memory usage of a process and store in *usage_out. */
+int pm_process_usage(pm_process_t *proc, pm_memusage_t *usage_out);
+
+/* Get the total memory usage of a process and store in *usage_out, only
+ * counting pages with specified flags. */
+int pm_process_usage_flags(pm_process_t *proc, pm_memusage_t *usage_out,
+                        uint64_t flags_mask, uint64_t required_flags);
+
+/* Get the working set of a process (if ws_out != NULL), and reset it
+ * (if reset != 0). */
+int pm_process_workingset(pm_process_t *proc, pm_memusage_t *ws_out, int reset);
+
+/* Get the PFNs corresponding to a range of virtual addresses.
+ * The array of PFNs is returned through *range_out, and the caller has the 
+ * responsibility to free it. */
+int pm_process_pagemap_range(pm_process_t *proc,
+                             uint64_t low, uint64_t hi,
+                             uint64_t **range_out, size_t *len);
+
+#define _BITS(x, offset, bits) (((x) >> (offset)) & ((1LL << (bits)) - 1))
+
+#define PM_PAGEMAP_PRESENT(x)     (_BITS(x, 63, 1))
+#define PM_PAGEMAP_SWAPPED(x)     (_BITS(x, 62, 1))
+#define PM_PAGEMAP_SHIFT(x)       (_BITS(x, 55, 6))
+#define PM_PAGEMAP_PFN(x)         (_BITS(x, 0, 55))
+#define PM_PAGEMAP_SWAP_OFFSET(x) (_BITS(x, 5, 50))
+#define PM_PAGEMAP_SWAP_TYPE(x)   (_BITS(x, 0,  5))
+
+/* Get the maps in the virtual address space of this process.
+ * Returns an array of pointers to pm_map_t through *maps.
+ * The array should be freed by the caller, but the maps should not be 
+ * modified or destroyed. */
+int pm_process_maps(pm_process_t *proc, pm_map_t ***maps_out, size_t *len);
+
+/* Destroy a pm_process_t. */
+int pm_process_destroy(pm_process_t *proc);
+
+/* Get the name, flags, start/end address, or offset of a map. */
+#define pm_map_name(map)   ((map)->name)
+#define pm_map_flags(map)  ((map)->flags)
+#define PM_MAP_READ  1
+#define PM_MAP_WRITE 2
+#define PM_MAP_EXEC  4
+#define PM_MAP_PERMISSIONS (PM_MAP_READ | PM_MAP_WRITE | PM_MAP_EXEC)
+#define pm_map_start(map)  ((map)->start)
+#define pm_map_end(map)    ((map)->end)
+#define pm_map_offset(map) ((map)->offset)
+
+/* Get the PFNs of the pages in the virtual address space of this map.
+ * Array of PFNs is returned through *pagemap_out, and should be freed by the
+ * caller. */
+int pm_map_pagemap(pm_map_t *map, uint64_t **pagemap_out, size_t *len);
+
+/* Get the memory usage of this map alone. */
+int pm_map_usage(pm_map_t *map, pm_memusage_t *usage_out);
+
+/* Get the memory usage of this map alone, only counting pages with specified
+ * flags. */
+int pm_map_usage_flags(pm_map_t *map, pm_memusage_t *usage_out,
+                        uint64_t flags_mask, uint64_t required_flags);
+
+/* Get the working set of this map alone. */
+int pm_map_workingset(pm_map_t *map, pm_memusage_t *ws_out);
+
+__END_DECLS
+
+#endif
diff --git a/libpagemap/pagemap_test.cpp b/libpagemap/pagemap_test.cpp
new file mode 100644
index 0000000..592072c
--- /dev/null
+++ b/libpagemap/pagemap_test.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <pagemap/pagemap.h>
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+TEST(pagemap, maps) {
+  pm_kernel_t* kernel;
+  ASSERT_EQ(0, pm_kernel_create(&kernel));
+
+  pm_process_t* process;
+  ASSERT_EQ(0, pm_process_create(kernel, getpid(), &process));
+
+  pm_map_t** maps;
+  size_t num_maps;
+  ASSERT_EQ(0, pm_process_maps(process, &maps, &num_maps));
+
+  bool found_heap = false;
+  bool found_stack = false;
+  for (size_t i = 0; i < num_maps; i++) {
+    std::string name(maps[i]->name);
+    if (name == "[heap]" || name == "[anon:libc_malloc]") found_heap = true;
+    if (name == "[stack]") found_stack = true;
+  }
+
+  ASSERT_TRUE(found_heap);
+  ASSERT_TRUE(found_stack);
+
+  free(maps);
+  pm_process_destroy(process);
+  pm_kernel_destroy(kernel);
+}
diff --git a/libpagemap/pm_kernel.c b/libpagemap/pm_kernel.c
new file mode 100644
index 0000000..b9e4e69
--- /dev/null
+++ b/libpagemap/pm_kernel.c
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <pagemap/pagemap.h>
+
+int pm_kernel_create(pm_kernel_t **ker_out) {
+    pm_kernel_t *ker;
+    int error;
+
+    if (!ker_out)
+        return 1;
+    
+    ker = calloc(1, sizeof(*ker));
+    if (!ker)
+        return errno;
+
+    ker->kpagecount_fd = open("/proc/kpagecount", O_RDONLY);
+    if (ker->kpagecount_fd < 0) {
+        error = errno;
+        free(ker);
+        return error;
+    }
+
+    ker->kpageflags_fd = open("/proc/kpageflags", O_RDONLY);
+    if (ker->kpageflags_fd < 0) {
+        error = errno;
+        close(ker->kpagecount_fd);
+        free(ker);
+        return error;
+    }
+
+    ker->pagesize = getpagesize();
+
+    *ker_out = ker;
+
+    return 0;
+}
+
+#define INIT_PIDS 20
+int pm_kernel_pids(pm_kernel_t *ker, pid_t **pids_out, size_t *len) {
+    DIR *proc;
+    struct dirent *dir;
+    pid_t pid, *pids, *new_pids;
+    size_t pids_count, pids_size;
+    int error;
+
+    proc = opendir("/proc");
+    if (!proc)
+        return errno;
+
+    pids = malloc(INIT_PIDS * sizeof(pid_t));
+    if (!pids) {
+        closedir(proc);
+        return errno;
+    }
+    pids_count = 0; pids_size = INIT_PIDS;
+
+    while ((dir = readdir(proc))) {
+        if (sscanf(dir->d_name, "%d", &pid) < 1)
+            continue;
+
+        if (pids_count >= pids_size) {
+            new_pids = realloc(pids, 2 * pids_size * sizeof(pid_t));
+            if (!new_pids) {
+                error = errno;
+                free(pids);
+                closedir(proc);
+                return error;
+            }
+            pids = new_pids;
+            pids_size = 2 * pids_size;
+        }
+
+        pids[pids_count] = pid;
+
+        pids_count++;
+    }
+
+    closedir(proc);
+    
+    new_pids = realloc(pids, pids_count * sizeof(pid_t));
+    if (!new_pids) {
+        error = errno;
+        free(pids);
+        return error;
+    }
+
+    *pids_out = new_pids;
+    *len = pids_count;
+
+    return 0;
+}
+
+int pm_kernel_count(pm_kernel_t *ker, uint64_t pfn, uint64_t *count_out) {
+    off64_t off;
+
+    if (!ker || !count_out)
+        return -1;
+
+    off = lseek64(ker->kpagecount_fd, pfn * sizeof(uint64_t), SEEK_SET);
+    if (off == (off_t)-1)
+        return errno;
+    if (read(ker->kpagecount_fd, count_out, sizeof(uint64_t)) <
+        (ssize_t)sizeof(uint64_t))
+        return errno;
+
+    return 0;
+}
+
+int pm_kernel_flags(pm_kernel_t *ker, uint64_t pfn, uint64_t *flags_out) {
+    off64_t off;
+
+    if (!ker || !flags_out)
+        return -1;
+
+    off = lseek64(ker->kpageflags_fd, pfn * sizeof(uint64_t), SEEK_SET);
+    if (off == (off_t)-1)
+        return errno;
+    if (read(ker->kpageflags_fd, flags_out, sizeof(uint64_t)) <
+        (ssize_t)sizeof(uint64_t))
+        return errno;
+
+    return 0;
+}
+
+int pm_kernel_destroy(pm_kernel_t *ker) {
+    if (!ker)
+        return -1;
+
+    close(ker->kpagecount_fd);
+    close(ker->kpageflags_fd);
+
+    free(ker);
+
+    return 0;
+}
diff --git a/libpagemap/pm_map.c b/libpagemap/pm_map.c
new file mode 100644
index 0000000..301a1cc
--- /dev/null
+++ b/libpagemap/pm_map.c
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <pagemap/pagemap.h>
+
+int pm_map_pagemap(pm_map_t *map, uint64_t **pagemap_out, size_t *len) {
+    if (!map)
+        return -1;
+
+    return pm_process_pagemap_range(map->proc, map->start, map->end,
+                                    pagemap_out, len);
+}
+
+int pm_map_usage_flags(pm_map_t *map, pm_memusage_t *usage_out,
+                        uint64_t flags_mask, uint64_t required_flags) {
+    uint64_t *pagemap;
+    size_t len, i;
+    uint64_t count;
+    pm_memusage_t usage;
+    int error;
+
+    if (!map || !usage_out)
+        return -1;
+
+    error = pm_map_pagemap(map, &pagemap, &len);
+    if (error) return error;
+
+    pm_memusage_zero(&usage);
+    pm_memusage_pswap_init_handle(&usage, usage_out->p_swap);
+
+    for (i = 0; i < len; i++) {
+        usage.vss += map->proc->ker->pagesize;
+
+        if (!PM_PAGEMAP_PRESENT(pagemap[i]) &&
+                !PM_PAGEMAP_SWAPPED(pagemap[i]))
+            continue;
+
+        if (!PM_PAGEMAP_SWAPPED(pagemap[i])) {
+            if (flags_mask) {
+                uint64_t flags;
+                error = pm_kernel_flags(map->proc->ker, PM_PAGEMAP_PFN(pagemap[i]),
+                                        &flags);
+                if (error) goto out;
+
+                if ((flags & flags_mask) != required_flags)
+                    continue;
+            }
+
+            error = pm_kernel_count(map->proc->ker, PM_PAGEMAP_PFN(pagemap[i]),
+                                    &count);
+            if (error) goto out;
+
+            usage.rss += (count >= 1) ? map->proc->ker->pagesize : (0);
+            usage.pss += (count >= 1) ? (map->proc->ker->pagesize / count) : (0);
+            usage.uss += (count == 1) ? (map->proc->ker->pagesize) : (0);
+        } else {
+            usage.swap += map->proc->ker->pagesize;
+            pm_memusage_pswap_add_offset(&usage, PM_PAGEMAP_SWAP_OFFSET(pagemap[i]));
+        }
+    }
+
+    memcpy(usage_out, &usage, sizeof(usage));
+
+    error = 0;
+
+out:
+    free(pagemap);
+
+    return error;
+}
+
+int pm_map_usage(pm_map_t *map, pm_memusage_t *usage_out) {
+    return pm_map_usage_flags(map, usage_out, 0, 0);
+}
+
+int pm_map_workingset(pm_map_t *map, pm_memusage_t *ws_out) {
+    uint64_t *pagemap;
+    size_t len, i;
+    uint64_t count, flags;
+    pm_memusage_t ws;
+    int error;
+
+    if (!map || !ws_out)
+        return -1;
+
+    error = pm_map_pagemap(map, &pagemap, &len);
+    if (error) return error;
+
+    pm_memusage_zero(&ws);
+
+    for (i = 0; i < len; i++) {
+        error = pm_kernel_flags(map->proc->ker, PM_PAGEMAP_PFN(pagemap[i]),
+                                &flags);
+        if (error) goto out;
+
+        if (!(flags & PM_PAGE_REFERENCED))
+            continue;
+
+        error = pm_kernel_count(map->proc->ker, PM_PAGEMAP_PFN(pagemap[i]),
+                                &count);
+        if (error) goto out;
+
+        ws.vss += map->proc->ker->pagesize;
+        if( PM_PAGEMAP_SWAPPED(pagemap[i]) ) continue;
+        ws.rss += (count >= 1) ? (map->proc->ker->pagesize) : (0);
+        ws.pss += (count >= 1) ? (map->proc->ker->pagesize / count) : (0);
+        ws.uss += (count == 1) ? (map->proc->ker->pagesize) : (0);
+    }
+
+    memcpy(ws_out, &ws, sizeof(ws));
+
+    error = 0;
+
+out:
+    free(pagemap);
+
+    return 0;
+}
+
+int pm_map_destroy(pm_map_t *map) {
+    if (!map)
+        return -1;
+
+    free(map->name);
+    free(map);
+
+    return 0;
+}
diff --git a/libpagemap/pm_map.h b/libpagemap/pm_map.h
new file mode 100644
index 0000000..08dc448
--- /dev/null
+++ b/libpagemap/pm_map.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBS_PAGEMAP_PM_MAP_H
+#define _LIBS_PAGEMAP_PM_MAP_H
+
+#include <pagemap/pagemap.h>
+
+int pm_map_destroy(pm_map_t *map);
+
+#endif
diff --git a/libpagemap/pm_memusage.c b/libpagemap/pm_memusage.c
new file mode 100644
index 0000000..71a5783
--- /dev/null
+++ b/libpagemap/pm_memusage.c
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <pagemap/pagemap.h>
+
+#define SIMPLEQ_INSERT_SIMPLEQ_TAIL(head_a, head_b)             \
+    do {                                                        \
+        if (!SIMPLEQ_EMPTY(head_b)) {                           \
+            if ((head_a)->sqh_first == NULL)                    \
+                (head_a)->sqh_first = (head_b)->sqh_first;      \
+            *(head_a)->sqh_last = (head_b)->sqh_first;          \
+            (head_a)->sqh_last = (head_b)->sqh_last;            \
+        }                                                       \
+    } while (/*CONSTCOND*/0)
+
+/* We use an array of int to store the references to a given offset in the swap
+   1 GiB swap means 512KiB size array: offset are the index */
+typedef unsigned short pm_pswap_refcount_t;
+struct pm_proportional_swap {
+    unsigned int array_size;
+    pm_pswap_refcount_t *offset_array;
+};
+
+void pm_memusage_zero(pm_memusage_t *mu) {
+    mu->vss = mu->rss = mu->pss = mu->uss = mu->swap = 0;
+    mu->p_swap = NULL;
+    SIMPLEQ_INIT(&mu->swap_offset_list);
+}
+
+void pm_memusage_pswap_init_handle(pm_memusage_t *mu, pm_proportional_swap_t *p_swap) {
+    mu->p_swap = p_swap;
+}
+
+void pm_memusage_add(pm_memusage_t *a, pm_memusage_t *b) {
+    a->vss += b->vss;
+    a->rss += b->rss;
+    a->pss += b->pss;
+    a->uss += b->uss;
+    a->swap += b->swap;
+    SIMPLEQ_INSERT_SIMPLEQ_TAIL(&a->swap_offset_list, &b->swap_offset_list);
+}
+
+pm_proportional_swap_t * pm_memusage_pswap_create(int swap_size)
+{
+    pm_proportional_swap_t *p_swap = NULL;
+
+    p_swap = malloc(sizeof(pm_proportional_swap_t));
+    if (p_swap == NULL) {
+        fprintf(stderr, "Error allocating proportional swap.\n");
+    } else {
+        p_swap->array_size = swap_size / getpagesize();
+        p_swap->offset_array = calloc(p_swap->array_size, sizeof(pm_pswap_refcount_t));
+        if (p_swap->offset_array == NULL) {
+            fprintf(stderr, "Error allocating proportional swap offset array.\n");
+            free(p_swap);
+            p_swap = NULL;
+        }
+    }
+
+    return p_swap;
+}
+
+void pm_memusage_pswap_destroy(pm_proportional_swap_t *p_swap) {
+    if (p_swap) {
+        free(p_swap->offset_array);
+        free(p_swap);
+    }
+}
+
+void pm_memusage_pswap_add_offset(pm_memusage_t *mu, unsigned int offset) {
+    pm_swap_offset_t *soff;
+
+    if (mu->p_swap == NULL)
+        return;
+
+    if (offset >= mu->p_swap->array_size) {
+        fprintf(stderr, "SWAP offset %d is out of swap bounds.\n", offset);
+        return;
+    }
+
+    if (mu->p_swap->offset_array[offset] == USHRT_MAX) {
+        fprintf(stderr, "SWAP offset %d ref. count if overflowing ushort type.\n", offset);
+    } else {
+        mu->p_swap->offset_array[offset]++;
+    }
+
+    soff = malloc(sizeof(pm_swap_offset_t));
+    if (soff) {
+        soff->offset = offset;
+        SIMPLEQ_INSERT_TAIL(&mu->swap_offset_list, soff, simpleqe);
+    }
+}
+
+void pm_memusage_pswap_get_usage(pm_memusage_t *mu, pm_swapusage_t *su) {
+
+    int pagesize = getpagesize();
+    pm_swap_offset_t *elem;
+
+    if (su == NULL)
+        return;
+
+    su->proportional = su->unique = 0;
+    SIMPLEQ_FOREACH(elem, &mu->swap_offset_list, simpleqe) {
+        su->proportional += pagesize / mu->p_swap->offset_array[elem->offset];
+        su->unique += mu->p_swap->offset_array[elem->offset] == 1 ? pagesize : 0;
+    }
+}
+
+void pm_memusage_pswap_free(pm_memusage_t *mu) {
+    pm_swap_offset_t *elem = SIMPLEQ_FIRST(&mu->swap_offset_list);
+    while (elem) {
+        SIMPLEQ_REMOVE_HEAD(&mu->swap_offset_list, simpleqe);
+        free(elem);
+        elem = SIMPLEQ_FIRST(&mu->swap_offset_list);
+    }
+}
diff --git a/libpagemap/pm_process.c b/libpagemap/pm_process.c
new file mode 100644
index 0000000..3c5c391
--- /dev/null
+++ b/libpagemap/pm_process.c
@@ -0,0 +1,331 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <pagemap/pagemap.h>
+
+#include "pm_map.h"
+
+static int read_maps(pm_process_t *proc);
+
+#define MAX_FILENAME 64
+
+int pm_process_create(pm_kernel_t *ker, pid_t pid, pm_process_t **proc_out) {
+    pm_process_t *proc;
+    char filename[MAX_FILENAME];
+    int error;
+
+    if (!ker || !proc_out)
+        return -1;
+
+    proc = calloc(1, sizeof(*proc));
+    if (!proc)
+        return errno;
+
+    proc->ker = ker;
+    proc->pid = pid;
+
+    error = snprintf(filename, MAX_FILENAME, "/proc/%d/pagemap", pid);
+    if (error < 0 || error >= MAX_FILENAME) {
+        error = (error < 0) ? (errno) : (-1);
+        free(proc);
+        return error;
+    }
+
+    proc->pagemap_fd = open(filename, O_RDONLY);
+    if (proc->pagemap_fd < 0) {
+        error = errno;
+        free(proc);
+        return error;
+    }        
+
+    error = read_maps(proc);
+    if (error) {
+        free(proc);
+        return error;
+    }
+
+    *proc_out = proc;
+
+    return 0;
+}
+
+int pm_process_usage_flags(pm_process_t *proc, pm_memusage_t *usage_out,
+                        uint64_t flags_mask, uint64_t required_flags)
+{
+    pm_memusage_t usage, map_usage;
+    int error;
+    int i;
+
+    if (!proc || !usage_out)
+        return -1;
+
+    pm_memusage_zero(&usage);
+    pm_memusage_pswap_init_handle(&usage, usage_out->p_swap);
+
+    pm_memusage_zero(&map_usage);
+    pm_memusage_pswap_init_handle(&map_usage, usage_out->p_swap);
+
+    for (i = 0; i < proc->num_maps; i++) {
+        error = pm_map_usage_flags(proc->maps[i], &map_usage, flags_mask,
+                                   required_flags);
+        if (error) return error;
+
+        pm_memusage_add(&usage, &map_usage);
+    }
+
+    memcpy(usage_out, &usage, sizeof(pm_memusage_t));
+
+    return 0;
+
+}
+
+int pm_process_usage(pm_process_t *proc, pm_memusage_t *usage_out) {
+    return pm_process_usage_flags(proc, usage_out, 0, 0);
+}
+
+int pm_process_pagemap_range(pm_process_t *proc,
+                             uint64_t low, uint64_t high,
+                             uint64_t **range_out, size_t *len) {
+    uint64_t firstpage;
+    uint64_t numpages;
+    uint64_t *range;
+    off64_t off;
+    int error;
+
+    if (!proc || (low > high) || !range_out || !len)
+        return -1;
+
+    if (low == high) {
+        *range_out = NULL;
+        *len = 0;
+        return 0;
+    }
+
+    firstpage = low / proc->ker->pagesize;
+    numpages = (high - low) / proc->ker->pagesize;
+
+    range = malloc(numpages * sizeof(uint64_t));
+    if (!range)
+        return errno;
+
+    off = lseek64(proc->pagemap_fd, firstpage * sizeof(uint64_t), SEEK_SET);
+    if (off == (off_t)-1) {
+        error = errno;
+        free(range);
+        return error;
+    }
+    error = read(proc->pagemap_fd, (char*)range, numpages * sizeof(uint64_t));
+    if (error == 0) {
+        /* EOF, mapping is not in userspace mapping range (probably vectors) */
+        *len = 0;
+        free(range);
+        *range_out = NULL;
+        return 0;
+    } else if (error < 0 || (error > 0 && error < (int)(numpages * sizeof(uint64_t)))) {
+        error = (error < 0) ? errno : -1;
+        free(range);
+        return error;
+    }
+
+    *range_out = range;
+    *len = numpages;
+
+    return 0;
+}
+
+int pm_process_maps(pm_process_t *proc, pm_map_t ***maps_out, size_t *len) {
+    pm_map_t **maps;
+
+    if (!proc || !maps_out || !len)
+        return -1;
+
+    if (proc->num_maps) {
+        maps = malloc(proc->num_maps * sizeof(pm_map_t*));
+        if (!maps)
+            return errno;
+
+        memcpy(maps, proc->maps, proc->num_maps * sizeof(pm_map_t*));
+    
+        *maps_out = maps;
+    } else {
+        *maps_out = NULL;
+    }
+    *len = proc->num_maps;
+
+    return 0;
+}
+
+int pm_process_workingset(pm_process_t *proc,
+                          pm_memusage_t *ws_out, int reset) {
+    pm_memusage_t ws, map_ws;
+    char filename[MAX_FILENAME];
+    int fd;
+    int i, j;
+    int error;
+
+    if (!proc)
+        return -1;
+
+    if (ws_out) {
+        pm_memusage_zero(&ws);
+        pm_memusage_pswap_init_handle(&ws, ws_out->p_swap);
+
+        pm_memusage_zero(&map_ws);
+        pm_memusage_pswap_init_handle(&map_ws, ws_out->p_swap);
+
+        for (i = 0; i < proc->num_maps; i++) {
+            error = pm_map_workingset(proc->maps[i], &map_ws);
+            if (error) return error;
+
+            pm_memusage_add(&ws, &map_ws);
+        }
+        
+        memcpy(ws_out, &ws, sizeof(ws));
+    }
+
+    if (reset) {
+        error = snprintf(filename, MAX_FILENAME, "/proc/%d/clear_refs",
+                         proc->pid);
+        if (error < 0 || error >= MAX_FILENAME) {
+            return (error < 0) ? (errno) : (-1);
+        }
+
+        fd = open(filename, O_WRONLY);
+        if (fd < 0)
+            return errno;
+
+        write(fd, "1\n", strlen("1\n"));
+
+        close(fd);
+    }
+
+    return 0;
+}
+
+int pm_process_destroy(pm_process_t *proc) {
+    int i;
+
+    if (!proc)
+        return -1;
+
+    for (i = 0; i < proc->num_maps; i++) {
+        pm_map_destroy(proc->maps[i]);
+    }
+    free(proc->maps);
+    close(proc->pagemap_fd);
+    free(proc);
+
+    return 0;
+}
+
+#define INITIAL_MAPS 10
+#define MAX_PERMS 5
+
+static int read_maps(pm_process_t *proc) {
+    char filename[MAX_FILENAME];
+    char *line = NULL;
+    size_t line_length = 0;
+    char perms[MAX_PERMS];
+    FILE *maps_f;
+    pm_map_t *map, **maps, **new_maps;
+    int maps_count, maps_size;
+    int error;
+       
+    if (!proc)
+        return -1;
+
+    maps = calloc(INITIAL_MAPS, sizeof(pm_map_t*));
+    if (!maps)
+        return errno;
+    maps_count = 0; maps_size = INITIAL_MAPS;
+
+    error = snprintf(filename, MAX_FILENAME, "/proc/%d/maps", proc->pid);
+    if (error < 0 || error >= MAX_FILENAME) {
+        free(maps);
+        return (error < 0) ? (errno) : (-1);
+    }
+
+    maps_f = fopen(filename, "r");
+    if (!maps_f) {
+        free(maps);
+        return errno;
+    }
+
+    while (getline(&line, &line_length, maps_f) != -1) {
+        line[strlen(line) - 1] = '\0'; // Lose the newline.
+
+        if (maps_count >= maps_size) {
+            new_maps = realloc(maps, 2 * maps_size * sizeof(pm_map_t*));
+            if (!new_maps) {
+                error = errno;
+                free(maps);
+                free(line);
+                fclose(maps_f);
+                return error;
+            }
+            maps = new_maps;
+            maps_size *= 2;
+        }
+
+        maps[maps_count] = map = calloc(1, sizeof(*map));
+
+        map->proc = proc;
+
+        int name_offset;
+        sscanf(line, "%" SCNx64 "-%" SCNx64 " %4s %" SCNx64 " %*s %*d %n",
+               &map->start, &map->end, perms, &map->offset, &name_offset);
+
+        map->name = strdup(line + name_offset);
+        if (!map->name) {
+            error = errno;
+            for (; maps_count > 0; maps_count--)
+                pm_map_destroy(maps[maps_count]);
+            free(maps);
+            free(line);
+            fclose(maps_f);
+            return error;
+        }
+
+        if (perms[0] == 'r') map->flags |= PM_MAP_READ;
+        if (perms[1] == 'w') map->flags |= PM_MAP_WRITE;
+        if (perms[2] == 'x') map->flags |= PM_MAP_EXEC;
+
+        maps_count++;
+    }
+
+    free(line);
+    fclose(maps_f);
+
+    new_maps = realloc(maps, maps_count * sizeof(pm_map_t*));
+    if (maps_count && !new_maps) {
+        error = errno;
+        free(maps);
+        return error;
+    }
+
+    proc->maps = new_maps;
+    proc->num_maps = maps_count;
+
+    return 0;
+}