diff options
author | Colin Cross | 2013-09-06 18:41:40 -0500 |
---|---|---|
committer | Colin Cross | 2013-09-06 20:18:35 -0500 |
commit | fc600e49bf9e0977b8e91642480e065bd95f2670 (patch) | |
tree | 1726cfb3ba8257f0f6155a25191be8fe1f51a2fb /libmemtrack | |
parent | f34861346d5c207912075fba9874090e4c947869 (diff) | |
download | platform-system-core-fc600e49bf9e0977b8e91642480e065bd95f2670.tar.gz platform-system-core-fc600e49bf9e0977b8e91642480e065bd95f2670.tar.xz platform-system-core-fc600e49bf9e0977b8e91642480e065bd95f2670.zip |
Initial libmemtrack
libmemtrack is an interface to a device-specific Memory Tracker HAL
to account for memory that may not show up in the normal memory
usage tools that walk /proc/pid/maps.
Bug: 10294768
Change-Id: I436f6799898df0bf8bf29747be3bc9dea5721185
Diffstat (limited to 'libmemtrack')
-rw-r--r-- | libmemtrack/Android.mk | 20 | ||||
-rw-r--r-- | libmemtrack/include/memtrack.h | 138 | ||||
-rw-r--r-- | libmemtrack/memtrack.c | 200 | ||||
-rw-r--r-- | libmemtrack/memtrack_test.c | 145 |
4 files changed, 503 insertions, 0 deletions
diff --git a/libmemtrack/Android.mk b/libmemtrack/Android.mk new file mode 100644 index 000000000..c23b6f410 --- /dev/null +++ b/libmemtrack/Android.mk | |||
@@ -0,0 +1,20 @@ | |||
1 | # Copyright 2013 The Android Open Source Project | ||
2 | |||
3 | LOCAL_PATH:= $(call my-dir) | ||
4 | |||
5 | include $(CLEAR_VARS) | ||
6 | LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include | ||
7 | LOCAL_SRC_FILES := memtrack.c | ||
8 | LOCAL_MODULE := libmemtrack | ||
9 | LOCAL_C_INCLUDES += $(LOCAL_PATH)/include hardware/libhardware/include | ||
10 | LOCAL_SHARED_LIBRARIES := libhardware liblog | ||
11 | LOCAL_CFLAGS := -Wall -Werror | ||
12 | include $(BUILD_SHARED_LIBRARY) | ||
13 | |||
14 | include $(CLEAR_VARS) | ||
15 | LOCAL_SRC_FILES := memtrack_test.c | ||
16 | LOCAL_MODULE := memtrack_test | ||
17 | LOCAL_C_INCLUDES := $(call include-path-for, libpagemap) | ||
18 | LOCAL_SHARED_LIBRARIES := libmemtrack libpagemap | ||
19 | LOCAL_CFLAGS := -Wall -Werror | ||
20 | include $(BUILD_EXECUTABLE) | ||
diff --git a/libmemtrack/include/memtrack.h b/libmemtrack/include/memtrack.h new file mode 100644 index 000000000..d6b370b22 --- /dev/null +++ b/libmemtrack/include/memtrack.h | |||
@@ -0,0 +1,138 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2013 The Android Open Source Project | ||
3 | * | ||
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | * you may not use this file except in compliance with the License. | ||
6 | * You may obtain a copy of the License at | ||
7 | * | ||
8 | * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | * | ||
10 | * Unless required by applicable law or agreed to in writing, software | ||
11 | * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | * See the License for the specific language governing permissions and | ||
14 | * limitations under the License. | ||
15 | */ | ||
16 | |||
17 | #ifndef _LIBMEMTRACK_MEMTRACK_H_ | ||
18 | #define _LIBMEMTRACK_MEMTRACK_H_ | ||
19 | |||
20 | #include <sys/types.h> | ||
21 | #include <stddef.h> | ||
22 | |||
23 | /** | ||
24 | * struct memtrack_proc | ||
25 | * | ||
26 | * an opaque handle to the memory stats on a process. | ||
27 | * Created with memtrack_proc_new, destroyed by | ||
28 | * memtrack_proc_destroy. Can be reused multiple times with | ||
29 | * memtrack_proc_get. | ||
30 | */ | ||
31 | struct memtrack_proc; | ||
32 | |||
33 | /** | ||
34 | * memtrack_init | ||
35 | * | ||
36 | * Must be called once before calling any other functions. After this function | ||
37 | * is called, everything else is thread-safe. | ||
38 | * | ||
39 | * Returns 0 on success, -errno on error. | ||
40 | */ | ||
41 | int memtrack_init(void); | ||
42 | |||
43 | /** | ||
44 | * memtrack_proc_new | ||
45 | * | ||
46 | * Return a new handle to hold process memory stats. | ||
47 | * | ||
48 | * Returns NULL on error. | ||
49 | */ | ||
50 | struct memtrack_proc *memtrack_proc_new(void); | ||
51 | |||
52 | /** | ||
53 | * memtrack_proc_destroy | ||
54 | * | ||
55 | * Free all memory associated with a process memory stats handle. | ||
56 | */ | ||
57 | void memtrack_proc_destroy(struct memtrack_proc *p); | ||
58 | |||
59 | /** | ||
60 | * memtrack_proc_get | ||
61 | * | ||
62 | * Fill a process memory stats handle with data about the given pid. Can be | ||
63 | * called on a handle that was just allocated with memtrack_proc_new, | ||
64 | * or on a handle that has been previously passed to memtrack_proc_get | ||
65 | * to replace the data with new data on the same or another process. It is | ||
66 | * expected that the second call on the same handle should not require | ||
67 | * allocating any new memory. | ||
68 | * | ||
69 | * Returns 0 on success, -errno on error. | ||
70 | */ | ||
71 | int memtrack_proc_get(struct memtrack_proc *p, pid_t pid); | ||
72 | |||
73 | /** | ||
74 | * memtrack_proc_graphics_total | ||
75 | * | ||
76 | * Return total amount of memory that has been allocated for use as window | ||
77 | * buffers. Does not differentiate between memory that has already been | ||
78 | * accounted for by reading /proc/pid/smaps and memory that has not been | ||
79 | * accounted for. | ||
80 | * | ||
81 | * Returns non-negative size in bytes on success, -errno on error. | ||
82 | */ | ||
83 | ssize_t memtrack_proc_graphics_total(struct memtrack_proc *p); | ||
84 | |||
85 | /** | ||
86 | * memtrack_proc_graphics_pss | ||
87 | * | ||
88 | * Return total amount of memory that has been allocated for use as window | ||
89 | * buffers, but has not already been accounted for by reading /proc/pid/smaps. | ||
90 | * Memory that is shared across processes may already be divided by the | ||
91 | * number of processes that share it (preferred), or may be charged in full to | ||
92 | * every process that shares it, depending on the capabilities of the driver. | ||
93 | * | ||
94 | * Returns non-negative size in bytes on success, -errno on error. | ||
95 | */ | ||
96 | ssize_t memtrack_proc_graphics_pss(struct memtrack_proc *p); | ||
97 | |||
98 | /** | ||
99 | * memtrack_proc_gl_total | ||
100 | * | ||
101 | * Same as memtrack_proc_graphics_total, but counts GL memory (which | ||
102 | * should not overlap with graphics memory) instead of graphics memory. | ||
103 | * | ||
104 | * Returns non-negative size in bytes on success, -errno on error. | ||
105 | */ | ||
106 | ssize_t memtrack_proc_gl_total(struct memtrack_proc *p); | ||
107 | |||
108 | /** | ||
109 | * memtrack_proc_gl_pss | ||
110 | * | ||
111 | * Same as memtrack_proc_graphics_total, but counts GL memory (which | ||
112 | * should not overlap with graphics memory) instead of graphics memory. | ||
113 | * | ||
114 | * Returns non-negative size in bytes on success, -errno on error. | ||
115 | */ | ||
116 | ssize_t memtrack_proc_gl_pss(struct memtrack_proc *p); | ||
117 | |||
118 | /** | ||
119 | * memtrack_proc_gl_total | ||
120 | * | ||
121 | * Same as memtrack_proc_graphics_total, but counts miscellaneous memory | ||
122 | * not tracked by gl or graphics calls above. | ||
123 | * | ||
124 | * Returns non-negative size in bytes on success, -errno on error. | ||
125 | */ | ||
126 | ssize_t memtrack_proc_other_total(struct memtrack_proc *p); | ||
127 | |||
128 | /** | ||
129 | * memtrack_proc_gl_pss | ||
130 | * | ||
131 | * Same as memtrack_proc_graphics_total, but counts miscellaneous memory | ||
132 | * not tracked by gl or graphics calls above. | ||
133 | * | ||
134 | * Returns non-negative size in bytes on success, -errno on error. | ||
135 | */ | ||
136 | ssize_t memtrack_proc_other_pss(struct memtrack_proc *p); | ||
137 | |||
138 | #endif | ||
diff --git a/libmemtrack/memtrack.c b/libmemtrack/memtrack.c new file mode 100644 index 000000000..2b2651a28 --- /dev/null +++ b/libmemtrack/memtrack.c | |||
@@ -0,0 +1,200 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2013 The Android Open Source Project | ||
3 | * | ||
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | * you may not use this file except in compliance with the License. | ||
6 | * You may obtain a copy of the License at | ||
7 | * | ||
8 | * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | * | ||
10 | * Unless required by applicable law or agreed to in writing, software | ||
11 | * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | * See the License for the specific language governing permissions and | ||
14 | * limitations under the License. | ||
15 | */ | ||
16 | |||
17 | #include <memtrack.h> | ||
18 | |||
19 | #define LOG_TAG "memtrack" | ||
20 | |||
21 | #include <log/log.h> | ||
22 | |||
23 | #include <hardware/memtrack.h> | ||
24 | |||
25 | #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) | ||
26 | |||
27 | static const memtrack_module_t *module; | ||
28 | |||
29 | struct memtrack_proc { | ||
30 | pid_t pid; | ||
31 | struct memtrack_proc_type { | ||
32 | enum memtrack_type type; | ||
33 | size_t num_records; | ||
34 | size_t allocated_records; | ||
35 | struct memtrack_record *records; | ||
36 | } types[MEMTRACK_NUM_TYPES]; | ||
37 | }; | ||
38 | |||
39 | int memtrack_init(void) | ||
40 | { | ||
41 | int err; | ||
42 | |||
43 | if (module) { | ||
44 | return 0; | ||
45 | } | ||
46 | |||
47 | err = hw_get_module(MEMTRACK_HARDWARE_MODULE_ID, | ||
48 | (hw_module_t const**)&module); | ||
49 | if (err) { | ||
50 | ALOGE("Couldn't load %s module (%s)", MEMTRACK_HARDWARE_MODULE_ID, | ||
51 | strerror(-err)); | ||
52 | return err; | ||
53 | } | ||
54 | |||
55 | return module->init(module); | ||
56 | } | ||
57 | |||
58 | struct memtrack_proc *memtrack_proc_new(void) | ||
59 | { | ||
60 | if (!module) { | ||
61 | return NULL; | ||
62 | } | ||
63 | |||
64 | return calloc(sizeof(struct memtrack_proc), 1); | ||
65 | } | ||
66 | |||
67 | void memtrack_proc_destroy(struct memtrack_proc *p) | ||
68 | { | ||
69 | enum memtrack_type i; | ||
70 | |||
71 | if (p) { | ||
72 | for (i = 0; i < MEMTRACK_NUM_TYPES; i++) { | ||
73 | free(p->types[i].records); | ||
74 | } | ||
75 | } | ||
76 | free(p); | ||
77 | } | ||
78 | |||
79 | static int memtrack_proc_get_type(struct memtrack_proc_type *t, | ||
80 | pid_t pid, enum memtrack_type type) | ||
81 | { | ||
82 | size_t num_records = t->num_records; | ||
83 | int ret; | ||
84 | |||
85 | retry: | ||
86 | ret = module->getMemory(module, pid, type, t->records, &num_records); | ||
87 | if (ret) { | ||
88 | t->num_records = 0; | ||
89 | return ret; | ||
90 | } | ||
91 | if (num_records > t->allocated_records) { | ||
92 | /* Need more records than allocated */ | ||
93 | free(t->records); | ||
94 | t->records = calloc(sizeof(*t->records), num_records); | ||
95 | if (!t->records) { | ||
96 | return -ENOMEM; | ||
97 | } | ||
98 | t->allocated_records = num_records; | ||
99 | goto retry; | ||
100 | } | ||
101 | t->num_records = num_records; | ||
102 | |||
103 | return 0; | ||
104 | } | ||
105 | |||
106 | /* TODO: sanity checks on return values from HALs: | ||
107 | * make sure no records have invalid flags set | ||
108 | * - unknown flags | ||
109 | * - too many flags of a single category | ||
110 | * - missing ACCOUNTED/UNACCOUNTED | ||
111 | * make sure there are not overlapping SHARED and SHARED_PSS records | ||
112 | */ | ||
113 | static int memtrack_proc_sanity_check(struct memtrack_proc *p) | ||
114 | { | ||
115 | (void)p; | ||
116 | return 0; | ||
117 | } | ||
118 | |||
119 | int memtrack_proc_get(struct memtrack_proc *p, pid_t pid) | ||
120 | { | ||
121 | enum memtrack_type i; | ||
122 | |||
123 | if (!module) { | ||
124 | return -EINVAL; | ||
125 | } | ||
126 | |||
127 | if (!p) { | ||
128 | return -EINVAL; | ||
129 | } | ||
130 | |||
131 | p->pid = pid; | ||
132 | for (i = 0; i < MEMTRACK_NUM_TYPES; i++) { | ||
133 | memtrack_proc_get_type(&p->types[i], pid, i); | ||
134 | } | ||
135 | |||
136 | return memtrack_proc_sanity_check(p); | ||
137 | } | ||
138 | |||
139 | static ssize_t memtrack_proc_sum(struct memtrack_proc *p, | ||
140 | enum memtrack_type types[], size_t num_types, | ||
141 | unsigned int flags) | ||
142 | { | ||
143 | ssize_t sum = 0; | ||
144 | size_t i; | ||
145 | size_t j; | ||
146 | |||
147 | for (i = 0; i < num_types; i++) { | ||
148 | enum memtrack_type type = types[i]; | ||
149 | for (j = 0; j < p->types[type].num_records; j++) { | ||
150 | if ((p->types[type].records[j].flags & flags) == flags) { | ||
151 | sum += p->types[type].records[j].size_in_bytes; | ||
152 | } | ||
153 | } | ||
154 | } | ||
155 | |||
156 | return sum; | ||
157 | } | ||
158 | |||
159 | ssize_t memtrack_proc_graphics_total(struct memtrack_proc *p) | ||
160 | { | ||
161 | enum memtrack_type types[] = { MEMTRACK_TYPE_GRAPHICS }; | ||
162 | return memtrack_proc_sum(p, types, ARRAY_SIZE(types), 0); | ||
163 | } | ||
164 | |||
165 | ssize_t memtrack_proc_graphics_pss(struct memtrack_proc *p) | ||
166 | { | ||
167 | enum memtrack_type types[] = { MEMTRACK_TYPE_GRAPHICS }; | ||
168 | return memtrack_proc_sum(p, types, ARRAY_SIZE(types), | ||
169 | MEMTRACK_FLAG_SMAPS_UNACCOUNTED); | ||
170 | } | ||
171 | |||
172 | ssize_t memtrack_proc_gl_total(struct memtrack_proc *p) | ||
173 | { | ||
174 | enum memtrack_type types[] = { MEMTRACK_TYPE_GL }; | ||
175 | return memtrack_proc_sum(p, types, ARRAY_SIZE(types), 0); | ||
176 | } | ||
177 | |||
178 | ssize_t memtrack_proc_gl_pss(struct memtrack_proc *p) | ||
179 | { | ||
180 | enum memtrack_type types[] = { MEMTRACK_TYPE_GL }; | ||
181 | return memtrack_proc_sum(p, types, ARRAY_SIZE(types), | ||
182 | MEMTRACK_FLAG_SMAPS_UNACCOUNTED); | ||
183 | } | ||
184 | |||
185 | ssize_t memtrack_proc_other_total(struct memtrack_proc *p) | ||
186 | { | ||
187 | enum memtrack_type types[] = { MEMTRACK_TYPE_MULTIMEDIA, | ||
188 | MEMTRACK_TYPE_CAMERA, | ||
189 | MEMTRACK_TYPE_OTHER }; | ||
190 | return memtrack_proc_sum(p, types, ARRAY_SIZE(types), 0); | ||
191 | } | ||
192 | |||
193 | ssize_t memtrack_proc_other_pss(struct memtrack_proc *p) | ||
194 | { | ||
195 | enum memtrack_type types[] = { MEMTRACK_TYPE_MULTIMEDIA, | ||
196 | MEMTRACK_TYPE_CAMERA, | ||
197 | MEMTRACK_TYPE_OTHER }; | ||
198 | return memtrack_proc_sum(p, types, ARRAY_SIZE(types), | ||
199 | MEMTRACK_FLAG_SMAPS_UNACCOUNTED); | ||
200 | } | ||
diff --git a/libmemtrack/memtrack_test.c b/libmemtrack/memtrack_test.c new file mode 100644 index 000000000..f306f67f1 --- /dev/null +++ b/libmemtrack/memtrack_test.c | |||
@@ -0,0 +1,145 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2013 The Android Open Source Project | ||
3 | * | ||
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | * you may not use this file except in compliance with the License. | ||
6 | * You may obtain a copy of the License at | ||
7 | * | ||
8 | * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | * | ||
10 | * Unless required by applicable law or agreed to in writing, software | ||
11 | * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | * See the License for the specific language governing permissions and | ||
14 | * limitations under the License. | ||
15 | */ | ||
16 | |||
17 | #include <stdio.h> | ||
18 | #include <stdlib.h> | ||
19 | #include <string.h> | ||
20 | #include <sys/types.h> | ||
21 | |||
22 | #include <memtrack.h> | ||
23 | |||
24 | #include <pagemap/pagemap.h> | ||
25 | |||
26 | #define DIV_ROUND_UP(x,y) (((x) + (y) - 1) / (y)) | ||
27 | |||
28 | static int getprocname(pid_t pid, char *buf, int len) { | ||
29 | char *filename; | ||
30 | FILE *f; | ||
31 | int rc = 0; | ||
32 | static const char* unknown_cmdline = "<unknown>"; | ||
33 | |||
34 | if (len <= 0) { | ||
35 | return -1; | ||
36 | } | ||
37 | |||
38 | if (asprintf(&filename, "/proc/%zd/cmdline", pid) < 0) { | ||
39 | rc = 1; | ||
40 | goto exit; | ||
41 | } | ||
42 | |||
43 | f = fopen(filename, "r"); | ||
44 | if (f == NULL) { | ||
45 | rc = 2; | ||
46 | goto releasefilename; | ||
47 | } | ||
48 | |||
49 | if (fgets(buf, len, f) == NULL) { | ||
50 | rc = 3; | ||
51 | goto closefile; | ||
52 | } | ||
53 | |||
54 | closefile: | ||
55 | (void) fclose(f); | ||
56 | releasefilename: | ||
57 | free(filename); | ||
58 | exit: | ||
59 | if (rc != 0) { | ||
60 | /* | ||
61 | * The process went away before we could read its process name. Try | ||
62 | * to give the user "<unknown>" here, but otherwise they get to look | ||
63 | * at a blank. | ||
64 | */ | ||
65 | if (strlcpy(buf, unknown_cmdline, (size_t)len) >= (size_t)len) { | ||
66 | rc = 4; | ||
67 | } | ||
68 | } | ||
69 | |||
70 | return rc; | ||
71 | } | ||
72 | |||
73 | int main(int argc, char *argv[]) | ||
74 | { | ||
75 | int ret; | ||
76 | pm_kernel_t *ker; | ||
77 | size_t num_procs; | ||
78 | pid_t *pids; | ||
79 | struct memtrack_proc *p; | ||
80 | size_t i; | ||
81 | |||
82 | (void)argc; | ||
83 | (void)argv; | ||
84 | |||
85 | ret = memtrack_init(); | ||
86 | if (ret < 0) { | ||
87 | fprintf(stderr, "failed to initialize HAL: %s (%d)\n", strerror(-ret), ret); | ||
88 | exit(EXIT_FAILURE); | ||
89 | } | ||
90 | |||
91 | ret = pm_kernel_create(&ker); | ||
92 | if (ret) { | ||
93 | fprintf(stderr, "Error creating kernel interface -- " | ||
94 | "does this kernel have pagemap?\n"); | ||
95 | exit(EXIT_FAILURE); | ||
96 | } | ||
97 | |||
98 | ret = pm_kernel_pids(ker, &pids, &num_procs); | ||
99 | if (ret) { | ||
100 | fprintf(stderr, "Error listing processes.\n"); | ||
101 | exit(EXIT_FAILURE); | ||
102 | } | ||
103 | |||
104 | p = memtrack_proc_new(); | ||
105 | if (ret) { | ||
106 | fprintf(stderr, "failed to create memtrack process handle\n"); | ||
107 | exit(EXIT_FAILURE); | ||
108 | } | ||
109 | |||
110 | for (i = 0; i < num_procs; i++) { | ||
111 | pid_t pid = pids[i]; | ||
112 | char cmdline[256]; | ||
113 | size_t v1; | ||
114 | size_t v2; | ||
115 | size_t v3; | ||
116 | size_t v4; | ||
117 | size_t v5; | ||
118 | size_t v6; | ||
119 | |||
120 | getprocname(pid, cmdline, (int)sizeof(cmdline)); | ||
121 | |||
122 | ret = memtrack_proc_get(p, pid); | ||
123 | if (ret) { | ||
124 | fprintf(stderr, "failed to get memory info for pid %d: %s (%d)\n", | ||
125 | pid, strerror(-ret), ret); | ||
126 | continue; | ||
127 | } | ||
128 | |||
129 | v1 = DIV_ROUND_UP(memtrack_proc_graphics_total(p), 1024); | ||
130 | v2 = DIV_ROUND_UP(memtrack_proc_graphics_pss(p), 1024); | ||
131 | v3 = DIV_ROUND_UP(memtrack_proc_gl_total(p), 1024); | ||
132 | v4 = DIV_ROUND_UP(memtrack_proc_gl_pss(p), 1024); | ||
133 | v5 = DIV_ROUND_UP(memtrack_proc_other_total(p), 1024); | ||
134 | v6 = DIV_ROUND_UP(memtrack_proc_other_pss(p), 1024); | ||
135 | |||
136 | if (v1 | v2 | v3 | v4 | v5 | v6) { | ||
137 | printf("%5d %6zu %6zu %6zu %6zu %6zu %6zu %s\n", pid, | ||
138 | v1, v2, v3, v4, v5, v6, cmdline); | ||
139 | } | ||
140 | } | ||
141 | |||
142 | memtrack_proc_destroy(p); | ||
143 | |||
144 | return 0; | ||
145 | } | ||