M7350v1_en_gpl

This commit is contained in:
T
2024-09-09 08:52:07 +00:00
commit f9cc65cfda
65988 changed files with 26357421 additions and 0 deletions

View File

@ -0,0 +1,37 @@
ifeq ($(call is-vendor-board-platform,QCOM),true)
ifeq ($(TARGET_ARCH),arm)
LOCAL_PATH := $(call my-dir)
# the dlkm
DLKM_DIR := device/qcom/common/dlkm
include $(CLEAR_VARS)
LOCAL_MODULE := the_memory_prof_module.ko
LOCAL_MODULE_TAGS := debug
include $(DLKM_DIR)/AndroidKernelModule.mk
# the userspace test program
include $(CLEAR_VARS)
LOCAL_MODULE := memory_prof
LOCAL_SRC_FILES += memory_prof.c
LOCAL_C_INCLUDES := $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr/include/
LOCAL_ADDITIONAL_DEPENDENCIES := $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr
LOCAL_SHARED_LIBRARIES := \
libc \
libcutils \
libutils
LOCAL_MODULE_TAGS := optional debug
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/kernel-tests
include $(BUILD_EXECUTABLE)
# the test script
include $(CLEAR_VARS)
LOCAL_MODULE := memory_prof.sh
LOCAL_MODULE_CLASS := EXECUTABLES
LOCAL_SRC_FILES := memory_prof.sh
LOCAL_MODULE_TAGS := optional debug
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/kernel-tests
include $(BUILD_PREBUILT)
endif
endif

View File

@ -0,0 +1,3 @@
obj-m := the_memory_prof_module.o
the_memory_prof_module-y := memory_prof_module.o timing_debug.o

View File

@ -0,0 +1,26 @@
memory_profdir = $(prefix)/memory_prof
memory_prof_PROGRAMS = memory_prof
memory_prof_SOURCES = memory_prof.c
memory_prof_CFLAGS = -lm
dist_memory_prof_SCRIPTS = memory_prof.sh run.sh
KERNEL_FLAGS ?= ARCH=arm
all_modules = the_memory_prof_module.ko
kmake = $(MAKE) $(KERNEL_FLAGS) -C $(KERNEL_DIR) M=$(CURDIR)
the_memory_prof_module.ko: memory_prof_module.c timing_debug.c
$(kmake) modules
all-local: $(all_modules)
install-exec-local: $(all_modules)
$(kmake) INSTALL_MOD_PATH=$(DESTDIR)$(prefix)/modules modules_install
# "make distclean" will always run clean-local in this directory,
# regardless of the KERNELMODULES conditional. Therefore, ensure
# KERNEL_DIR exists before running clean. Further, don't fail even
# if there is a problem.
clean-local:
-test ! -d "$(KERNEL_DIR)" || $(kmake) clean

View File

@ -0,0 +1,35 @@
Test: memory_prof
Usage: memory_prof [OPTIONS]...
OPTIONS:
-h Print this message and exit
-a Do the adversarial test (same as -l)
-b Do basic sanity tests
-e Do Ion heap profiling
-k Do kernel alloc profiling (requires kernel module)
-l Do leak test (leak an ion handle)
-m Do map extra test (requires kernel module)
-n Do the nominal test (same as -b)
-o Do OOM test (alloc from Ion Iommu heap until OOM)
-p MS Sleep for MS milliseconds between stuff (for debugging)
-r Do the repeatability test
-s Do the stress test (same as -e)
Description:
These tests are useful for catching performance regressions in Ion or
general memory code (using the -e and -k options). They can also catch
other Ion regressions by performing some basic sanity tests (the -b,
-m, and -l options).
Notes:
This test suite is accompanied by a kernel module that must be
inserted for certain test cases (namely -k and -m). The memory_prof.sh
script will take care of inserting the kernel module and running the
memory_prof binary for you. However, sometimes it's useful to be able
run the memory_prof binary directly without inserting the kernel
module.
Target support: 8974

View File

@ -0,0 +1,894 @@
/*
* Copyright (c) 2013, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of The Linux Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <unistd.h>
#include <math.h>
#include <libgen.h> /* for basename */
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/queue.h>
#include <linux/msm_ion.h>
#include <memory_prof_module.h>
#define NUM_REPS_FOR_HEAP_PROFILING 100
#define NUM_REPS_FOR_REPEATABILITY 100
#define ION_PRE_ALLOC_SIZE_DEFAULT 0 //0 MB
#define MAX_PRE_ALLOC_SIZE 5000 // 5000 MB
#define MEMORY_PROF_DEV "/dev/memory_prof"
#define ION_DEV "/dev/ion"
/*
* Don't change the format of the following line strings. We need to
* rely on them for parsing.
*/
#define ST_PREFIX_DATA_ROW "=> "
#define ST_PREFIX_PREALLOC_SIZE "==> "
#define ST_PREFIX_NUM_REPS "===> "
#define SZ_1K 0x00000400
#define SZ_2K 0x00000800
#define SZ_4K 0x00001000
#define SZ_8K 0x00002000
#define SZ_16K 0x00004000
#define SZ_32K 0x00008000
#define SZ_64K 0x00010000
#define SZ_128K 0x00020000
#define SZ_256K 0x00040000
#define SZ_512K 0x00080000
#define SZ_1M 0x00100000
#define SZ_2M 0x00200000
#define SZ_4M 0x00400000
#define SZ_8M 0x00800000
#define SZ_16M 0x01000000
#define SZ_32M 0x02000000
#define SZ_64M 0x04000000
#define SZ_128M 0x08000000
#define SZ_256M 0x10000000
#define SZ_512M 0x20000000
static unsigned int sleepiness;
static void sleepy()
{
usleep(sleepiness);
}
static void print_n_slow(char *st)
{
puts(st);
sleepy();
}
static void hr()
{
puts("---------------");
}
/**
* @ionfd [out] ion fd. On success, user must close.
* @alloc_data [in/out] alloc data. On success, user must free.
* returns 0 on success, 1 otherwise
*/
static int alloc_me_up_some_ion(int *ionfd,
struct ion_allocation_data *alloc_data)
{
int rc = 0;
*ionfd = open(ION_DEV, O_RDONLY);
if (*ionfd < 0) {
perror("couldn't open " ION_DEV);
return *ionfd;
}
rc = ioctl(*ionfd, ION_IOC_ALLOC, alloc_data);
if (rc) {
perror("couldn't do ion alloc");
close(*ionfd);
}
return rc;
}
static int basic_ion_sanity_test(struct ion_allocation_data alloc_data,
unsigned long size_mb)
{
uint8_t *buf;
int ionfd, rc;
unsigned long i, squelched = 0;
bool integrity_good = true;
struct ion_fd_data fd_data;
struct ion_custom_data custom_data;
struct ion_flush_data flush_data;
rc = alloc_me_up_some_ion(&ionfd, &alloc_data);
if (rc)
goto out;
fd_data.handle = alloc_data.handle;
rc = ioctl(ionfd, ION_IOC_MAP, &fd_data);
if (rc) {
perror("couldn't do ION_IOC_MAP");
goto err1;
}
buf = mmap(NULL, size_mb, PROT_READ | PROT_WRITE, MAP_SHARED,
fd_data.fd, 0);
if (buf == MAP_FAILED) {
perror("couldn't do mmap");
rc = (int) MAP_FAILED;
goto err2;
}
memset(buf, 0xA5, size_mb);
flush_data.handle = alloc_data.handle;
flush_data.vaddr = buf;
flush_data.length = size_mb;
custom_data.cmd = ION_IOC_CLEAN_INV_CACHES;
custom_data.arg = (unsigned long) &flush_data;
if (ioctl(ionfd, ION_IOC_CUSTOM, &custom_data)) {
perror("Couldn't flush caches");
rc = 1;
goto err3;
} else {
puts("flushed caches");
}
for (i = 0; i < size_mb; ++i) {
if (buf[i] != 0xA5) {
if (!integrity_good) {
squelched++;
continue;
}
printf(" Data integrity error at "
"offset 0x%x from 0x%p!!!!\n", i, buf);
integrity_good = false;
}
}
if (squelched)
printf(" (squelched %d additional integrity error%s)\n",
squelched, squelched == 1 ? "" : "s");
if (integrity_good)
printf(" Buffer integrity check succeeded\n");
err3:
if (munmap(buf, size_mb)) {
rc = 1;
perror("couldn't do munmap");
}
err2:
close(fd_data.fd);
err1:
rc |= ioctl(ionfd, ION_IOC_FREE, &alloc_data.handle);
err0:
close(ionfd);
out:
return rc;
}
static void basic_sanity_tests(unsigned long size_mb)
{
int lrc, rc = 0;
struct ion_allocation_data iommu_alloc_data = {
.align = SZ_1M,
.len = SZ_1M,
.heap_mask = ION_HEAP(ION_IOMMU_HEAP_ID),
.flags = 0,
};
struct ion_allocation_data system_alloc_data = {
.align = SZ_1M,
.len = SZ_1M,
.heap_mask = ION_HEAP(ION_SYSTEM_HEAP_ID),
.flags = 0,
};
struct ion_allocation_data system_contig_alloc_data = {
.align = SZ_1M,
.len = SZ_1M,
.heap_mask = ION_HEAP(ION_SYSTEM_CONTIG_HEAP_ID),
.flags = 0,
};
puts("testing IOMMU without caching...");
lrc = basic_ion_sanity_test(iommu_alloc_data, size_mb);
puts(lrc ? "FAILED!" : "PASSED");
hr();
sleepy();
rc |= lrc;
puts("testing IOMMU with caching...");
iommu_alloc_data.flags |= ION_FLAG_CACHED;
lrc = basic_ion_sanity_test(iommu_alloc_data, size_mb);
puts(lrc ? "FAILED!" : "PASSED");
hr();
sleepy();
rc |= lrc;
puts("testing system without caching (should fail)...");
lrc = !basic_ion_sanity_test(system_alloc_data, size_mb);
puts(lrc ? "FAILED! (failed to fail)" : "PASSED (successfully failed)");
hr();
sleepy();
rc |= lrc;
puts("testing system with caching...");
system_alloc_data.flags |= ION_FLAG_CACHED;
lrc = basic_ion_sanity_test(system_alloc_data, size_mb);
puts(lrc ? "FAILED!" : "PASSED");
hr();
sleepy();
rc |= lrc;
puts("testing system contig without caching (should fail)...");
lrc = !basic_ion_sanity_test(system_contig_alloc_data, size_mb);
puts(lrc ? "FAILED! (failed to fail)" : "PASSED (successfully failed)");
hr();
sleepy();
rc |= lrc;
puts("testing system contig with caching...");
system_contig_alloc_data.flags |= ION_FLAG_CACHED;
lrc = basic_ion_sanity_test(system_contig_alloc_data, size_mb);
puts(lrc ? "FAILED!" : "PASSED");
hr();
sleepy();
rc |= lrc;
if (rc)
puts("BASIC SANITY TESTS FAILED!!!!!!!! WOOOWWWW!!!!!!");
else
puts("All basic sanity tests passed");
}
static int do_map_extra_test(void)
{
int rc = 0;
int ionfd, memory_prof_fd;
struct memory_prof_map_extra_args args;
size_t buffer_length = SZ_1M;
struct ion_allocation_data alloc_data = {
.align = SZ_1M,
.len = buffer_length,
.heap_mask = ION_HEAP(ION_IOMMU_HEAP_ID),
.flags = 0,
};
struct ion_fd_data fd_data;
memory_prof_fd = open(MEMORY_PROF_DEV, 0);
if (memory_prof_fd < 0) {
perror("couldn't open " MEMORY_PROF_DEV);
rc = memory_prof_fd;
goto out;
}
rc = ioctl(memory_prof_fd, MEMORY_PROF_IOC_CLIENT_CREATE);
if (rc) {
perror("couldn't do MEMORY_PROF_IOC_CLIENT_CREATE");
goto close_memory_prof_fd;
}
rc = alloc_me_up_some_ion(&ionfd, &alloc_data);
if (rc)
goto destroy_ion_client;
fd_data.handle = alloc_data.handle;
rc = ioctl(ionfd, ION_IOC_SHARE, &fd_data);
if (rc) {
perror("Couldn't do ION_IOC_SHARE");
goto ion_free_and_close;
}
args.ionfd = fd_data.fd;
args.iommu_map_len = buffer_length * 2;
print_n_slow("Doing MEMORY_PROF_IOC_TEST_MAP_EXTRA");
rc = ioctl(memory_prof_fd, MEMORY_PROF_IOC_TEST_MAP_EXTRA, &args);
if (rc) {
perror("couldn't do MEMORY_PROF_IOC_TEST_MAP_EXTRA");
goto ion_free_and_close;
}
ion_free_and_close:
rc |= ioctl(ionfd, ION_IOC_FREE, &alloc_data.handle);
close(ionfd);
destroy_ion_client:
rc |= ioctl(memory_prof_fd, MEMORY_PROF_IOC_CLIENT_DESTROY);
close_memory_prof_fd:
close(memory_prof_fd);
out:
return rc;
}
#define US_TO_MS(us) (us / 1000)
#define MS_TO_S(ms) (ms / 1000)
#define S_TO_MS(s) (s * 1000)
#define MS_TO_US(ms) (ms * 1000)
#define S_TO_US(s) (s * 1000 * 1000)
/**
* timeval_sub - subtracts t2 from t1
*
* Returns a timeval with the result
*/
static struct timeval timeval_sub(struct timeval t1, struct timeval t2)
{
struct timeval diff;
diff.tv_sec = t1.tv_sec - t2.tv_sec;
if (t1.tv_usec < t2.tv_usec) {
diff.tv_usec = t1.tv_usec + S_TO_US(1) - t2.tv_usec;
diff.tv_sec--;
} else {
diff.tv_usec = t1.tv_usec - t2.tv_usec;
}
return diff;
}
/**
* timeval_ms_diff - gets the difference (in MS) between t1 and t2
*
* Returns the MS diff between t1 and t2 (t2 - t1)
*/
static long timeval_ms_diff(struct timeval t1, struct timeval t2)
{
struct timeval tv_result = timeval_sub(t1, t2);
return US_TO_MS(tv_result.tv_usec) + S_TO_MS(tv_result.tv_sec);
}
/**
* Free memory in alloc_list
*/
void free_mem_list(char **alloc_list)
{
int i = 0;
for (i = 0; i < MAX_PRE_ALLOC_SIZE; i++) {
if (alloc_list[i] != NULL) {
free(alloc_list[i]);
alloc_list[i] = NULL;
}
}
}
/**
* Allocate sizemb in alloc_list
*/
int alloc_mem_list(char **alloc_list, int sizemb)
{
int i = 0;
int alloc_size = 1*1024*1024 * sizeof(char);
if (sizemb > MAX_PRE_ALLOC_SIZE) {
return -1;
}
// Break allocation into 1 MB pieces to ensure
// we easily find enough virtually contigous memory
for (i = 0; i < sizemb; i++)
{
alloc_list[i] =(char *) malloc(alloc_size);
if (alloc_list[i] == NULL)
{
perror("couldn't allocate 1MB");
free_mem_list(alloc_list);
return -1;
}
// Memory must be accessed for it to be page backed.
// We may want to randomize the data in the future
// to prevent features such as KSM returning memory
// to the system.
memset(alloc_list[i], 1, alloc_size);
}
return 0;
}
/**
* Returns the total time taken for ION_IOC_ALLOC
*
* @heap_mask - passed to ION_IOC_ALLOC
* @flags - passed to ION_IOC_ALLOC
* @size - passed to ION_IOC_ALLOC
* @quiet - whether we should suppress alloc failures
* @pre_alloc_size - size of memory to malloc before doing the ion alloc
* @alloc_ms - [output] time taken to complete the ION_IOC_ALLOC
* @free_ms - [output] time taken to complete the ION_IOC_FREE
*
* Returns 0 on success, 1 on failure.
*/
static int profile_alloc_for_heap(unsigned int heap_mask,
unsigned int flags, unsigned int size,
bool quiet,
int pre_alloc_size,
long *alloc_ms, long *free_ms)
{
long ms = -1;
int ionfd, rc, rc2;
struct timeval tv_before, tv_after, tv_result;
struct ion_allocation_data alloc_data = {
.align = SZ_4K,
.len = size,
.heap_mask = heap_mask,
.flags = flags,
};
char *pre_alloc_list[MAX_PRE_ALLOC_SIZE];
memset(pre_alloc_list, 0, MAX_PRE_ALLOC_SIZE * sizeof(char *));
*alloc_ms = 0;
*free_ms = 0;
if (alloc_mem_list(pre_alloc_list, pre_alloc_size) < 0) {
perror("couldn't create pre-allocated buffer");
goto out;
}
ionfd = open(ION_DEV, O_RDONLY);
if (ionfd < 0) {
perror("couldn't open " ION_DEV);
goto free_mem_list;
}
rc = gettimeofday(&tv_before, NULL);
if (rc) {
perror("couldn't get time of day");
goto close_ion;
}
rc2 = ioctl(ionfd, ION_IOC_ALLOC, &alloc_data);
rc = gettimeofday(&tv_after, NULL);
if (rc) {
perror("couldn't get time of day");
goto close_ion;
}
if (rc2) {
if (!quiet)
perror("couldn't do ion alloc");
rc = rc2;
goto close_ion;
}
*alloc_ms = timeval_ms_diff(tv_after, tv_before);
/*
* Okay, things are about to get messy. We're profiling our
* cleanup, but we might fail some stuff and need to
* cleanup... during cleanup.
*/
rc = gettimeofday(&tv_before, NULL);
if (rc)
perror("couldn't get time of day");
free:
ioctl(ionfd, ION_IOC_FREE, &alloc_data.handle);
rc2 = gettimeofday(&tv_after, NULL);
if (!rc) {
if (rc2) {
perror("couldn't get time of day");
goto close_ion;
}
*free_ms = timeval_ms_diff(tv_after, tv_before);
}
close_ion:
close(ionfd);
free_mem_list:
free_mem_list(pre_alloc_list);
out:
return rc;
}
static void map_extra_test(void)
{
int rc = 0;
puts("testing msm_iommu_map_extra...");
rc = do_map_extra_test();
if (rc) puts("FAILED!");
hr();
}
static void profile_kernel_alloc(void)
{
int rc, memory_prof_fd;
memory_prof_fd = open(MEMORY_PROF_DEV, 0);
if (memory_prof_fd < 0) {
perror("couldn't open " MEMORY_PROF_DEV);
return;
}
rc = ioctl(memory_prof_fd, MEMORY_PROF_IOC_TEST_KERNEL_ALLOCS);
if (rc)
perror("couldn't do MEMORY_PROF_IOC_TEST_KERNEL_ALLOCS");
close(memory_prof_fd);
}
static void print_stats_results(const char *name, const char *cached,
const char *size_string,
long alloc_stats[], int reps)
{
int i;
float sum = 0, sum_of_squares = 0, average, std_dev;
for (i = 0; i < reps; ++i) {
sum += alloc_stats[i];
}
average = sum / reps;
for (i = 0; i < reps; ++i) {
sum_of_squares += pow(alloc_stats[i] - average, 2);
}
std_dev = sqrt(sum_of_squares / reps);
printf(ST_PREFIX_DATA_ROW " %s %s %s average: %.2f std_dev: %.2f\n",
name, cached, size_string, average, std_dev);
}
static void heap_profiling(int pre_alloc_size, const int nreps)
{
int i;
struct sizes_struct {
unsigned long size;
const char *sMB;
bool quiet;
};
struct sizes_struct *szp;
struct sizes_struct sizes[] = {
{
.size = SZ_1M * 1,
.sMB = "1MB",
.quiet = false,
}, {
.size = SZ_1M * 3,
.sMB = "3MB",
.quiet = false,
}, {
.size = SZ_1M * 5,
.sMB = "5MB",
.quiet = false,
}, {
.size = SZ_1M * 8,
.sMB = "8MB",
.quiet = false,
}, {
.size = SZ_1M * 10,
.sMB = "10MB",
.quiet = false,
}, {
.size = SZ_1M * 13,
.sMB = "13MB",
.quiet = false,
}, {
.size = SZ_1M * 20,
.sMB = "20MB",
.quiet = false,
}, {
.size = SZ_1M * 50,
.sMB = "50MB",
.quiet = true,
}, {
.size = SZ_1M * 100,
.sMB = "100MB",
.quiet = true,
}, {
/* SENTINEL */
.size = 0,
}
};
puts("All times are in milliseconds unless otherwise indicated");
printf(ST_PREFIX_PREALLOC_SIZE "pre-alloc size (MB): %d\n",
pre_alloc_size);
printf(ST_PREFIX_NUM_REPS "num reps: %d\n", nreps);
for (szp = &sizes[0]; szp->size; ++szp) {
unsigned int sz = szp->size;
const char *sMB = szp->sMB;
bool quiet = szp->quiet;
long alloc_stats[nreps];
long free_stats[nreps];
printf("\n============PROFILING FOR %s MB=============\n",
sMB);
for (i = 0; i < nreps; ++i) {
profile_alloc_for_heap(
ION_HEAP(ION_CP_MM_HEAP_ID),
ION_SECURE, sz, quiet,
pre_alloc_size,
&alloc_stats[i], &free_stats[i]);
}
print_stats_results("ION_IOC_ALLOC ION_CP_MM_HEAP_ID",
"uncached", sMB,
alloc_stats, nreps);
print_stats_results("ION_IOC_FREE ION_CP_MM_HEAP_ID",
"uncached", sMB,
free_stats, nreps);
for (i = 0; i < nreps; ++i) {
profile_alloc_for_heap(
ION_HEAP(ION_IOMMU_HEAP_ID),
0, sz, quiet,
pre_alloc_size,
&alloc_stats[i], &free_stats[i]);
}
print_stats_results("ION_IOC_ALLOC ION_IOMMU_HEAP_ID",
"uncached", sMB,
alloc_stats, nreps);
print_stats_results("ION_IOC_FREE ION_IOMMU_HEAP_ID",
"uncached", sMB,
free_stats, nreps);
for (i = 0; i < nreps; ++i) {
profile_alloc_for_heap(
ION_HEAP(ION_IOMMU_HEAP_ID),
ION_FLAG_CACHED, sz, quiet,
pre_alloc_size,
&alloc_stats[i], &free_stats[i]);
}
print_stats_results("ION_IOC_ALLOC ION_IOMMU_HEAP_ID",
"cached", sMB,
alloc_stats, nreps);
print_stats_results("ION_IOC_FREE ION_IOMMU_HEAP_ID",
"cached", sMB,
free_stats, nreps);
for (i = 0; i < nreps; ++i) {
profile_alloc_for_heap(
ION_HEAP(ION_SYSTEM_HEAP_ID),
ION_FLAG_CACHED, sz, quiet,
pre_alloc_size,
&alloc_stats[i], &free_stats[i]);
}
print_stats_results("ION_IOC_ALLOC ION_SYSTEM_HEAP_ID",
"cached", sMB,
alloc_stats, nreps);
print_stats_results("ION_IOC_FREE ION_SYSTEM_HEAP_ID",
"cached", sMB,
free_stats, nreps);
for (i = 0; i < nreps; ++i) {
profile_alloc_for_heap(
ION_HEAP(ION_SYSTEM_HEAP_ID),
0, sz, quiet,
pre_alloc_size,
&alloc_stats[i], &free_stats[i]);
}
print_stats_results("ION_IOC_ALLOC ION_SYSTEM_HEAP_ID",
"uncached", sMB,
alloc_stats, nreps);
print_stats_results("ION_IOC_FREE ION_SYSTEM_HEAP_ID",
"uncached", sMB,
free_stats, nreps);
}
}
static void oom_test(void)
{
int rc, ionfd, cnt = 0;
struct ion_allocation_data alloc_data = {
.len = SZ_8M,
.heap_mask = ION_HEAP(ION_IOMMU_HEAP_ID),
.flags = 0,
};
LIST_HEAD(handle_list, ion_handle_node) head;
struct handle_list *headp;
struct ion_handle_node {
struct ion_handle *handle;
LIST_ENTRY(ion_handle_node) nodes;
} *np;
LIST_INIT(&head);
ionfd = open(ION_DEV, O_RDONLY);
if (ionfd < 0) {
perror("couldn't open " ION_DEV);
return;
}
for (;; cnt++) {
rc = ioctl(ionfd, ION_IOC_ALLOC, &alloc_data);
if (rc) {
/* game over! */
break;
} else {
np = malloc(sizeof(struct ion_handle_node));
np->handle = alloc_data.handle;
LIST_INSERT_HEAD(&head, np, nodes);
}
printf("Allocated %d MB so far...\n", cnt * 8);
}
printf("Did %d 8M allocations before dying\n", cnt);
while (head.lh_first != NULL) {
np = head.lh_first;
ioctl(ionfd, ION_IOC_FREE, np->handle);
LIST_REMOVE(head.lh_first, nodes);
free(np);
}
}
static void leak_test(void)
{
int ionfd;
struct ion_fd_data fd_data;
struct ion_allocation_data alloc_data = {
.len = SZ_4K,
.heap_mask = ION_HEAP(ION_SYSTEM_HEAP_ID),
.flags = 0,
};
puts("About to leak a handle...");
fflush(stdout);
alloc_me_up_some_ion(&ionfd, &alloc_data);
fd_data.handle = alloc_data.handle;
if (ioctl(ionfd, ION_IOC_MAP, &fd_data))
perror("Couldn't ION_IOC_MAP the buffer");
close(ionfd);
puts("closed ionfd w/o free'ing handle.");
puts("If you have CONFIG_ION_LEAK_CHECK turned on in");
puts("your kernel and have already done:");
puts("echo 1 > /debug/ion/check_leaks_on_destroy");
puts("then you should have seen a warning on your");
puts("console.");
puts("We will now sleep for 5 seconds for you to check");
puts("<debugfs>/ion/check_leaked_fds");
sleep(5);
}
#define USAGE_STRING \
"Usage: %s [options]\n" \
"\n" \
"Supported options:\n" \
"\n" \
" -h Print this message and exit\n" \
" -a Do the adversarial test (same as -l)\n" \
" -b Do basic sanity tests\n" \
" -z Size (in MB) of buffer for basic sanity tests (default=1)\n" \
" -e[REPS] Do Ion heap profiling. Optionally specify number of reps\n" \
" E.g.: -e10 would do 10 reps (default=100)\n" \
" -k Do kernel alloc profiling (requires kernel module)\n" \
" -l Do leak test (leak an ion handle)\n" \
" -m Do map extra test (requires kernel module)\n" \
" -n Do the nominal test (same as -b)\n" \
" -o Do OOM test (alloc from Ion Iommu heap until OOM)\n" \
" -p MS Sleep for MS milliseconds between stuff (for debugging)\n" \
" -r Do the repeatability test\n" \
" -s Do the stress test (same as -e)\n" \
" -t MB Size (in MB) of temp buffer pre-allocated before Ion allocations (default 0 MB)\n"
static void usage(char *progname)
{
printf(USAGE_STRING, progname);
}
int main(int argc, char *argv[])
{
int rc = 0, i, opt;
unsigned long basic_sanity_size_mb = SZ_1M;
bool do_basic_sanity_tests = false;
bool do_heap_profiling = false;
bool do_kernel_alloc_profiling = false;
bool do_map_extra_test = false;
bool do_oom_test = false;
bool do_leak_test = false;
int num_reps = 1;
int num_heap_prof_reps = NUM_REPS_FOR_HEAP_PROFILING;
int ion_pre_alloc_size = ION_PRE_ALLOC_SIZE_DEFAULT;
while (-1 != (opt = getopt(argc, argv, "abe::hklmnop:rs::t:z:"))) {
switch (opt) {
case 't':
ion_pre_alloc_size = atoi(optarg);
break;
case 'n':
case 'b':
do_basic_sanity_tests = true;
break;
case 's':
case 'e':
if (optarg)
num_heap_prof_reps = atoi(optarg);
do_heap_profiling = true;
break;
case 'k':
do_kernel_alloc_profiling = true;
break;
case 'a':
case 'l':
do_leak_test = true;
break;
case 'm':
do_map_extra_test = true;
break;
case 'o':
do_oom_test = true;
break;
case 'r':
num_reps = NUM_REPS_FOR_REPEATABILITY;
break;
case 'p':
/* ms to us */
sleepiness = atoi(optarg) * 1000;
break;
case 'z':
basic_sanity_size_mb = atoi(optarg) * SZ_1M;
break;
case 'h':
default:
usage(basename(argv[0]));
exit(1);
}
}
if (do_basic_sanity_tests)
for (i = 0; i < num_reps; ++i)
basic_sanity_tests(basic_sanity_size_mb);
if (do_map_extra_test)
for (i = 0; i < num_reps; ++i)
map_extra_test();
if (do_heap_profiling)
for (i = 0; i < num_reps; ++i)
heap_profiling(ion_pre_alloc_size,
num_heap_prof_reps);
if (do_kernel_alloc_profiling)
for (i = 0; i < num_reps; ++i)
profile_kernel_alloc();
if (do_oom_test)
for (i = 0; i < num_reps; ++i)
oom_test();
if (do_leak_test)
for (i = 0; i < num_reps; ++i)
leak_test();
return rc;
}

View File

@ -0,0 +1,73 @@
#!/bin/sh
# Copyright (c) 2013, The Linux Foundation. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
# * Neither the name of The Linux Foundation nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
# IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
cd $(dirname $0 2>/dev/null || echo "/data/kernel-tests")
chmod 755 memory_prof
maybe_make_node()
{
thedev=$1
themisc=$2
[ -e $thedev ] || \
mknod $thedev c $(cut -d: -f1 $themisc) $(cut -d: -f2 $themisc)
}
if [ -d /system/lib/modules/ ]; then
modpath=/system/lib/modules
else
modpath=/kernel-tests/modules/lib/modules/$(uname -r)/extra
fi
memory_prof_mod=${modpath}/the_memory_prof_module.ko
memory_prof_dev=/dev/memory_prof
memory_prof_dev_sys=/sys/class/memory_prof/memory_prof/dev
ion_dev=/dev/ion
ion_dev_misc=/sys/class/misc/ion/dev
# create ion device if it doesn't exist
maybe_make_node $ion_dev $ion_dev_misc
# insert memory_prof_mod if needed
if [ ! -e $memory_prof_dev_sys ]; then
insmod $memory_prof_mod
if [ $? -ne 0 ]; then
echo "ERROR: failed to load module $memory_prof_mod"
exit 1
fi
fi
# create memory prof device if it doesn't exist
maybe_make_node $memory_prof_dev $memory_prof_dev_sys
./memory_prof $@
rmmod the_memory_prof_module

View File

@ -0,0 +1,338 @@
/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program 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.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/mm.h>
#include <asm/cacheflush.h>
#include <asm-generic/sizes.h>
#include <mach/iommu_domains.h>
#include <linux/msm_ion.h>
#include <asm/uaccess.h>
#include "memory_prof_module.h"
#include "timing_debug.h"
#define MEMORY_PROF_DEV_NAME "memory_prof"
struct memory_prof_module_data {
struct ion_client *client;
};
static struct class *memory_prof_class;
static int memory_prof_major;
static struct device *memory_prof_dev;
static void zero_the_pages_plz(struct page **pages, int npages,
unsigned int page_size)
{
void *ptr;
int len = npages * page_size;
ptr = vmap(pages, npages, VM_IOREMAP, pgprot_writecombine(PAGE_KERNEL));
if (ptr == NULL) {
WARN(1, "BOGUS vmap ERROR!!!\n");
} else {
memset(ptr, 0, len);
dmac_flush_range(ptr, ptr + len);
vunmap(ptr);
}
}
static void test_kernel_alloc(const char *tag, gfp_t thegfp,
unsigned int page_size,
void (*transform_page)(struct page *),
void (*transform_pages)(struct page **, int, unsigned int))
{
char st1[200];
char st2[200];
int i, j;
int order = get_order(page_size);
int size = (SZ_1M * 20);
int npages = PAGE_ALIGN(size) / page_size;
struct page **pages;
pages = kmalloc(npages * sizeof(struct page *),
GFP_KERNEL);
snprintf(st1, 200, "before %s%s", tag,
transform_page ? " (flush each)" : "");
snprintf(st2, 200, "after %s%s", tag,
transform_page ? " (flush each)" : "");
timing_debug_tick(st1);
for (i = 0; i < npages; ++i) {
pages[i] = alloc_pages(thegfp, order);
if (!pages[i]) {
WARN(1, "BOGUS alloc_pages ERROR!!!\n");
npages = i;
goto out;
}
if (transform_page)
for (j = 0; j < order; j++)
transform_page(nth_page(pages[i], j));
}
timing_debug_tock(st2);
if (transform_pages) {
timing_debug_tick("before transform_pages");
transform_pages(pages, npages, page_size);
timing_debug_tock("after transform_pages");
}
out:
for (i = 0; i < npages; ++i)
__free_pages(pages[i], order);
kfree(pages);
}
#define DO_TEST_KERNEL_ALLOC(thegfp, sz, transform_page, transform_pages) \
test_kernel_alloc(#thegfp " " #sz, thegfp, sz, \
transform_page, transform_pages)
static void test_kernel_allocs(void)
{
int i;
timing_debug_init();
timing_debug_start_new_timings();
pr_err("Testing small pages without flushing individual pages\n");
for (i = 0; i < 7; ++i)
DO_TEST_KERNEL_ALLOC(GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO,
PAGE_SIZE, NULL, NULL);
for (i = 0; i < 7; ++i)
DO_TEST_KERNEL_ALLOC(GFP_KERNEL | __GFP_HIGHMEM,
PAGE_SIZE, NULL, NULL);
for (i = 0; i < 7; ++i)
DO_TEST_KERNEL_ALLOC(GFP_KERNEL | __GFP_HIGHMEM,
PAGE_SIZE, NULL, zero_the_pages_plz);
for (i = 0; i < 7; ++i)
DO_TEST_KERNEL_ALLOC(GFP_KERNEL | __GFP_ZERO,
PAGE_SIZE, NULL, NULL);
for (i = 0; i < 7; ++i)
DO_TEST_KERNEL_ALLOC(GFP_KERNEL,
PAGE_SIZE, NULL, NULL);
for (i = 0; i < 7; ++i)
DO_TEST_KERNEL_ALLOC(GFP_KERNEL,
PAGE_SIZE, NULL, zero_the_pages_plz);
pr_err("Testing small pages with flushing individual pages\n");
for (i = 0; i < 7; ++i)
DO_TEST_KERNEL_ALLOC(GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO,
PAGE_SIZE, flush_dcache_page, NULL);
for (i = 0; i < 7; ++i)
DO_TEST_KERNEL_ALLOC(GFP_KERNEL | __GFP_HIGHMEM,
PAGE_SIZE, flush_dcache_page, NULL);
for (i = 0; i < 7; ++i)
DO_TEST_KERNEL_ALLOC(GFP_KERNEL | __GFP_HIGHMEM,
PAGE_SIZE, flush_dcache_page,
zero_the_pages_plz);
for (i = 0; i < 7; ++i)
DO_TEST_KERNEL_ALLOC(GFP_KERNEL | __GFP_ZERO,
PAGE_SIZE, flush_dcache_page, NULL);
for (i = 0; i < 7; ++i)
DO_TEST_KERNEL_ALLOC(GFP_KERNEL,
PAGE_SIZE, flush_dcache_page, NULL);
for (i = 0; i < 7; ++i)
DO_TEST_KERNEL_ALLOC(GFP_KERNEL,
PAGE_SIZE, flush_dcache_page,
zero_the_pages_plz);
pr_err("Testing with large page sizes without flushing individual pages\n");
for (i = 0; i < 7; ++i)
DO_TEST_KERNEL_ALLOC(
GFP_KERNEL | __GFP_HIGHMEM | __GFP_COMP | __GFP_ZERO,
SZ_64K, NULL, NULL);
for (i = 0; i < 7; ++i)
DO_TEST_KERNEL_ALLOC(
GFP_KERNEL | __GFP_HIGHMEM | __GFP_COMP,
SZ_64K, NULL, NULL);
for (i = 0; i < 7; ++i)
DO_TEST_KERNEL_ALLOC(
GFP_KERNEL | __GFP_COMP | __GFP_ZERO,
SZ_64K, NULL, NULL);
for (i = 0; i < 7; ++i)
DO_TEST_KERNEL_ALLOC(
GFP_KERNEL | __GFP_COMP,
SZ_64K, NULL, NULL);
pr_err("Testing with large page sizes with flushing individual pages\n");
for (i = 0; i < 7; ++i)
DO_TEST_KERNEL_ALLOC(
GFP_KERNEL | __GFP_HIGHMEM | __GFP_COMP | __GFP_ZERO,
SZ_64K, flush_dcache_page, NULL);
for (i = 0; i < 7; ++i)
DO_TEST_KERNEL_ALLOC(
GFP_KERNEL | __GFP_HIGHMEM | __GFP_COMP,
SZ_64K, flush_dcache_page, NULL);
for (i = 0; i < 7; ++i)
DO_TEST_KERNEL_ALLOC(
GFP_KERNEL | __GFP_COMP | __GFP_ZERO,
SZ_64K, flush_dcache_page, NULL);
for (i = 0; i < 7; ++i)
DO_TEST_KERNEL_ALLOC(
GFP_KERNEL | __GFP_COMP,
SZ_64K, flush_dcache_page, NULL);
timing_debug_dump_results();
}
static long memory_prof_test_ioctl(struct file *file, unsigned cmd, unsigned long arg)
{
int ret = 0;
struct memory_prof_module_data *module_data = file->private_data;
switch (cmd) {
case MEMORY_PROF_IOC_CLIENT_CREATE:
{
module_data->client = msm_ion_client_create(~0, "memory_prof_module");
if (IS_ERR_OR_NULL(module_data->client))
return -EIO;
break;
}
case MEMORY_PROF_IOC_CLIENT_DESTROY:
{
ion_client_destroy(module_data->client);
break;
}
case MEMORY_PROF_IOC_TEST_MAP_EXTRA:
{
struct memory_prof_map_extra_args args;
struct ion_handle *handle;
struct ion_client *client = module_data->client;
unsigned long iova, buffer_size;
if (copy_from_user(&args, (void __user *)arg,
sizeof(struct memory_prof_map_extra_args)))
return -EFAULT;
handle = ion_import_dma_buf(client, args.ionfd);
if (IS_ERR_OR_NULL(handle)) {
pr_err("Couldn't do ion_import_dma_buf in "
"MEMORY_PROF_IOC_TEST_MAP_EXTRA\n");
return -EINVAL;
}
ret = ion_map_iommu(client, handle, VIDEO_DOMAIN,
VIDEO_FIRMWARE_POOL, SZ_8K,
args.iommu_map_len, &iova, &buffer_size,
0, 0);
if (ret) {
pr_err("Couldn't ion_map_iommu in "
"MEMORY_PROF_IOC_TEST_MAP_EXTRA\n");
return ret;
}
break;
}
case MEMORY_PROF_IOC_TEST_KERNEL_ALLOCS:
{
test_kernel_allocs();
break;
}
default:
pr_info("command not supproted\n");
ret = -EINVAL;
}
return ret;
}
static int memory_prof_test_open(struct inode *inode, struct file *file)
{
struct memory_prof_module_data *module_data = kzalloc(
sizeof(struct memory_prof_module_data), GFP_KERNEL);
if (!module_data)
return -ENOMEM;
file->private_data = module_data;
pr_info("memory_prof test device opened\n");
return 0;
}
static int memory_prof_test_release(struct inode *inode, struct file *file)
{
struct memory_prof_module_data *module_data = file->private_data;
kfree(module_data);
pr_info("memory_prof test device closed\n");
return 0;
}
static const struct file_operations memory_prof_test_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = memory_prof_test_ioctl,
.open = memory_prof_test_open,
.release = memory_prof_test_release,
};
static int memory_prof_device_create(void)
{
int rc = 0;
memory_prof_major = register_chrdev(0, MEMORY_PROF_DEV_NAME,
&memory_prof_test_fops);
if (memory_prof_major < 0) {
rc = memory_prof_major;
pr_err("Unable to register chrdev: %d\n", memory_prof_major);
goto out;
}
memory_prof_class = class_create(THIS_MODULE, MEMORY_PROF_DEV_NAME);
if (IS_ERR(memory_prof_class)) {
rc = PTR_ERR(memory_prof_class);
pr_err("Unable to create class: %d\n", rc);
goto err_create_class;
}
memory_prof_dev = device_create(memory_prof_class, NULL,
MKDEV(memory_prof_major, 0),
NULL, MEMORY_PROF_DEV_NAME);
if (IS_ERR(memory_prof_dev)) {
rc = PTR_ERR(memory_prof_dev);
pr_err("Unable to create device: %d\n", rc);
goto err_create_device;
}
return rc;
err_create_device:
class_destroy(memory_prof_class);
err_create_class:
unregister_chrdev(memory_prof_major, MEMORY_PROF_DEV_NAME);
out:
return rc;
}
static void memory_prof_device_destroy(void)
{
device_destroy(memory_prof_class, MKDEV(memory_prof_major, 0));
class_destroy(memory_prof_class);
unregister_chrdev(memory_prof_major, MEMORY_PROF_DEV_NAME);
}
static int memory_prof_test_init(void)
{
return memory_prof_device_create();
}
static void memory_prof_test_exit(void)
{
return memory_prof_device_destroy();
}
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Profile memory stuff");
module_init(memory_prof_test_init);
module_exit(memory_prof_test_exit);

View File

@ -0,0 +1,32 @@
/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program 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.
*/
#ifndef _MEMORY_PROF_MODULE_H
#include <linux/ioctl.h>
struct memory_prof_map_extra_args {
int ionfd;
unsigned long iommu_map_len;
};
#define MEMORY_PROF_MAGIC 'H'
#define MEMORY_PROF_IOC_CLIENT_CREATE _IO(MEMORY_PROF_MAGIC, 0)
#define MEMORY_PROF_IOC_CLIENT_DESTROY _IO(MEMORY_PROF_MAGIC, 1)
#define MEMORY_PROF_IOC_TEST_MAP_EXTRA \
_IOR(MEMORY_PROF_MAGIC, 2, struct memory_prof_map_extra_args)
#define MEMORY_PROF_IOC_TEST_KERNEL_ALLOCS _IO(MEMORY_PROF_MAGIC, 3)
#endif /* #ifndef _MEMORY_PROF_MODULE_H */

View File

@ -0,0 +1,256 @@
#!/usr/bin/env python
# Copyright (c) 2013, The Linux Foundation. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
# * Neither the name of The Linux Foundation nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
# IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# Quick and very dirty parser for the output of `memory_prof.sh -e'
# This should be run on the host (not the target) with the output from
# the target piped in (e.g. copy/paste/cat) or passed as a file on the
# command line
# Dependencies: matplotlib, numpy
# NOTE: THIS HAS GOTTEN UNWIELDY. YOU HAVE BEEN WARNED.
import sys
import fileinput
import operator
from itertools import cycle
from optparse import OptionParser
import numpy as np
import matplotlib.pyplot as plt
ST_PREFIX_DATA_ROW = "=> "
ST_PREFIX_PREALLOC_SIZE = "==> "
ST_PREFIX_NUM_REPS = "===> "
def get_data_lines(data):
return [line.lstrip(ST_PREFIX_DATA_ROW).rstrip('\n') for line in data if line.startswith(ST_PREFIX_DATA_ROW)]
def extract_data_for_size(data, target_sz):
cached_timings = {
'ION_IOC_ALLOC': [],
'ION_IOC_FREE': [],
}
uncached_timings = {
'ION_IOC_ALLOC': [],
'ION_IOC_FREE': [],
}
heaps = []
for line in get_data_lines(data):
# for the format, see print_stats_results in memory_prof.c
(ion_op, heap_id, caching, sz, lbl_av, average, lbl_std, std_dev) = line.split(' ')
if sz != target_sz: continue
av = max(float(average), 0)
if heap_id not in heaps:
heaps.append(heap_id)
if caching == 'cached':
cached_timings[ion_op].append(av)
else:
uncached_timings[ion_op].append(av)
return (heaps, cached_timings, uncached_timings)
def extract_all_data(data):
timings = {
'ION_IOC_ALLOC': {},
'ION_IOC_FREE': {},
}
for line in get_data_lines(data):
# for the format, see print_stats_results in memory_prof.c
(ion_op, heap_id, caching, sz, lbl_av, average, lbl_std, std_dev) = line.split(' ')
timings[ion_op].setdefault(heap_id, {})
av = max(float(average), 0)
timings[ion_op][heap_id].setdefault(caching, {})
timings[ion_op][heap_id][caching][sz] = float(average)
return timings
def compare_heaps_for_a_size(data, target_sz, num_reps, pre_alloc_size, ion_op, text_only=False, target=None):
(heaps, cached_timings, uncached_timings) = extract_data_for_size(data, target_sz)
cached_timings = cached_timings[ion_op]
uncached_timings = uncached_timings[ion_op]
title = ('Ion %s times\n' % ion_op) \
+ ('Target: %s' % ("%s\n" % target) if target is not None else "") \
+ ('(%s with ION_IOC_ALLOC, average of %d reps, %dMB pre-allocation)' % (target_sz, num_reps, pre_alloc_size))
print title
print
for (heap, cached, uncached) in zip(heaps, cached_timings, uncached_timings):
print "%25s (cached): %f" % (heap, cached)
print "%25s (uncached): %s" % (heap, str(uncached) if uncached != 0 else "N/A")
if text_only:
return
ind = np.arange(len(heaps))
width = .35
fig = plt.figure()
ax = fig.add_subplot(111)
cached_rects = ax.bar(ind, cached_timings, width, color='r')
uncached_rects = ax.bar(ind + width, uncached_timings, width, color='y')
ax.set_title(title)
ax.set_ylabel('Time (ms)')
ax.set_xticks(ind + width)
ax.set_xticklabels(heaps)
ax.legend( (cached_rects[0], uncached_rects[0]), ('Cached', 'Uncached') )
plt.show()
def first_key_element(d):
"Returns the first element found in the dict `d'."
return d[d.keys()[0]]
def compare_times_for_heaps(data, num_reps, pre_alloc_size, ion_op, text_only=False, target=None):
timings = extract_all_data(data)[ion_op]
# we need to sort the size strings, which are a few levels in
# (through some keys that might or might not exist)
first_heap_timing = first_key_element(timings)
sizes_for_heap_timing = first_key_element(first_heap_timing)
sorted_keys = sorted(
sizes_for_heap_timing.keys(),
key=lambda v: float(v.split('MB')[0])
)
for target_heap in timings.keys():
heap_timings = timings[target_heap]
print '%s times for %s (%d reps, %dMB pre-allocation)\n' \
% (ion_op, target_heap, num_reps, pre_alloc_size)
format_str = '%6s %10s %10s'
print format_str % (
'Size', 'Cached', 'Uncached',
)
for k in sorted_keys:
print format_str % (
k,
('%5.2f' % heap_timings['cached'][k]) if 'cached' in heap_timings else 'NA',
('%5.2f' % heap_timings['uncached'][k]) if 'uncached' in heap_timings else 'NA',
)
print '\n'
if text_only:
return
fig = plt.figure()
ax = fig.add_subplot(111)
sorted_keys_numbers = [float(s.split('MB')[0]) for s in sorted_keys]
shapes_cycler = cycle(["o", "v", "^" , "<", ">"])
for target_heap in timings.keys():
for caching in ('cached', 'uncached'):
heap_timings = timings[target_heap]
if caching in heap_timings:
lbl = "%s (%s)" % (target_heap, caching)
ax.plot(sorted_keys_numbers,
[heap_timings[caching][k] for k in sorted_keys],
next(shapes_cycler) + '-',
label=lbl)
title = ("Ion %s times\n" % ion_op) \
+ ('%s' % ("Target: %s\n" % target) if target is not None else "") \
+ ("(average of %d reps)" % num_reps)
ax.set_ylabel("Time (ms)")
ax.set_xlabel("Allocation size (MB)")
ax.set_title(title)
ax.legend(loc="upper left")
plt.grid(True)
plt.show()
if __name__ == "__main__":
target_sz = None
parser = OptionParser()
parser.add_option("-c", "--compare-heaps", action="store_true",
help="Compare same-sized allocations across heaps")
parser.add_option("-z", "--compare-alloc-sizes", action="store_true",
help="Compare same-heap allocations across sizes")
parser.add_option("-s", "--size", metavar="SIZE",
help="Allocation size to plot (e.g. '8MB'). Only used with -c")
# parser.add_option("-e", "--heap", metavar="HEAP",
# help="Heap to plot (e.g. 'ION_CP_MM_HEAP_ID'), or 'ALL'. Only used with -z")
parser.add_option("-t", "--text-only", action="store_true")
parser.add_option("--target", help="Name of the device (used for plot titles)")
parser.add_option("-o", "--ion-op",
default="ION_IOC_ALLOC",
help="Ion operation to display (currently supported: ION_IOC_ALLOC, ION_IOC_FREE)")
(options, args) = parser.parse_args()
if options.compare_heaps and not options.size:
print "You must provide a size (-s) when comparing same-sized allocations across heaps (-c)"
sys.exit(1)
if not options.compare_heaps and not options.compare_alloc_sizes:
print "You must specify either -c or -z"
sys.exit(1)
# snarf:
data = [line for line in fileinput.input(args)]
# get the num reps:
repsline = [line for line in data if line.startswith(ST_PREFIX_NUM_REPS)][0]
num_reps = int(repsline.split(' ')[-1])
# get the pre-alloc size:
prealloclines = [line for line in data if line.startswith(ST_PREFIX_PREALLOC_SIZE)]
if len(prealloclines) > 0:
pre_alloc_size = int(prealloclines[0].split(' ')[-1])
else:
pre_alloc_size = 0
if options.compare_heaps:
compare_heaps_for_a_size(data, options.size, num_reps,
pre_alloc_size,
options.ion_op,
text_only=options.text_only,
target=options.target)
if options.compare_alloc_sizes:
compare_times_for_heaps(data, num_reps,
pre_alloc_size,
options.ion_op,
text_only=options.text_only,
target=options.target)

View File

@ -0,0 +1,39 @@
#!/bin/sh
# Copyright (c) 2013, The Linux Foundation. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
# * Neither the name of The Linux Foundation nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
# IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
cd $(dirname $0)
echo ":: Doing a basic sanity test"
./memory_prof.sh -b
echo ":: Doing Ion profiling"
./memory_prof.sh -e
echo ":: Doing kernel alloc profiling"
./memory_prof.sh -k

View File

@ -0,0 +1,115 @@
/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program 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.
*/
/**
* file: timing_debug.c
*
* A simple nesting timer module.
*
* IMPORTANT: THIS IS NOT THREAD SAFE!
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/time.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include "timing_debug.h"
#define MODULE_NAME "TIMING_DEBUG"
#define __TIMING_DEBUG_TIME_MAX_CLOCK_NESTING 100
#define __TIMING_DEBUG_TIME_MAX_TICKS_AND_TOCKS_PER_SESSION 5000
#define __TIMING_DEBUG_TIME_LINE_SIZE 100
static struct timespec __timing_debug_t1[__TIMING_DEBUG_TIME_MAX_CLOCK_NESTING];
static struct timespec __timing_debug_t2[__TIMING_DEBUG_TIME_MAX_CLOCK_NESTING];
static int __timing_debug_time_sp;
static int __timing_debug_time_timing_buf_cur;
static char *__timing_debug_timing_message_buffer[
__TIMING_DEBUG_TIME_MAX_TICKS_AND_TOCKS_PER_SESSION];
void timing_debug_start_new_timings(void)
{
__timing_debug_time_timing_buf_cur = 0;
}
void timing_debug_dump_results(void)
{
int i;
for (i = 0; i < __timing_debug_time_timing_buf_cur; ++i)
pr_err("[timing debug] %s",
__timing_debug_timing_message_buffer[i]);
}
static char *__timing_debug_get_spaces(void)
{
int i;
static char spaces[__TIMING_DEBUG_TIME_MAX_CLOCK_NESTING];
char *it = spaces;
for (i = 0; i < __timing_debug_time_sp; ++i)
*it++ = ' ';
*it++ = '\0';
return spaces;
}
void timing_debug_tick(char *msg)
{
snprintf(__timing_debug_timing_message_buffer[
__timing_debug_time_timing_buf_cur++],
__TIMING_DEBUG_TIME_LINE_SIZE,
"%stick %s\n", __timing_debug_get_spaces(), msg);
getnstimeofday(&__timing_debug_t1[__timing_debug_time_sp]);
__timing_debug_time_sp++;
}
void timing_debug_tock(char *msg)
{
struct timespec diff;
--__timing_debug_time_sp;
getnstimeofday(&__timing_debug_t2[__timing_debug_time_sp]);
diff = timespec_sub(__timing_debug_t2[__timing_debug_time_sp],
__timing_debug_t1[__timing_debug_time_sp]);
snprintf(__timing_debug_timing_message_buffer[
__timing_debug_time_timing_buf_cur++],
__TIMING_DEBUG_TIME_LINE_SIZE,
"%stock %s => Delta: %ld ms\n",
__timing_debug_get_spaces(), msg,
(long int) div_s64(timespec_to_ns(&diff), 1000000));
}
/* TODO: protect with a mutex */
static bool did_init;
int timing_debug_init(void)
{
int i, rc = 0;
if (did_init)
return 0;
for (i = 0;
i < __TIMING_DEBUG_TIME_MAX_TICKS_AND_TOCKS_PER_SESSION;
++i) {
void *buf = kmalloc(__TIMING_DEBUG_TIME_LINE_SIZE, GFP_KERNEL);
if(!buf) {
rc = -ENOMEM;
goto out;
}
__timing_debug_timing_message_buffer[i] = buf;
}
did_init = true;
out:
return rc;
}

View File

@ -0,0 +1,41 @@
/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program 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.
*/
#ifndef _TIMING_DEBUG_H
/**
* Init the timings system
*/
int timing_debug_init(void);
/**
* Start a new set of timings
*/
void timing_debug_start_new_timings(void);
/**
* Dump results from current set of timings.
*/
void timing_debug_dump_results(void);
/**
* Record tick.
*/
void timing_debug_tick(char *msg);
/**
* Record tock. timing_debug_dump_results will then show the difference
* between this tock and the last tick.
*/
void timing_debug_tock(char *msg);
#endif /* #ifndef _TIMING_DEBUG_H */