[PATCH] lib/amd: add memleak functions

vitaly.prosyak at amd.com vitaly.prosyak at amd.com
Tue Feb 18 11:43:41 UTC 2025


From: Vitaly Prosyak <vitaly.prosyak at amd.com>

refactor memory leak functions and add
them to the library for reuse across different tests.

Cc: Christian Koenig <christian.koenig at amd.com>
Cc: Alexander Deucher <alexander.deucher at amd.com>
Cc: Jesse Zhang <jesse.zhang at amd.com>
Cc: Harry Wentland <harry.wentland at amd.com>

Signed-off-by: Vitaly Prosyak <vitaly.prosyak at amd.com>
---
 lib/amdgpu/amd_mem_leak.c   | 112 ++++++++++++++++++++++++++++++++++++
 lib/amdgpu/amd_mem_leak.h   |  17 ++++++
 lib/meson.build             |   1 +
 tests/amdgpu/amd_mem_leak.c |  88 ++--------------------------
 4 files changed, 135 insertions(+), 83 deletions(-)
 create mode 100644 lib/amdgpu/amd_mem_leak.c
 create mode 100644 lib/amdgpu/amd_mem_leak.h

diff --git a/lib/amdgpu/amd_mem_leak.c b/lib/amdgpu/amd_mem_leak.c
new file mode 100644
index 000000000..a367b4eab
--- /dev/null
+++ b/lib/amdgpu/amd_mem_leak.c
@@ -0,0 +1,112 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2020 Advanced Micro Devices, Inc.
+ * Copyright 2025 Advanced Micro Devices, Inc.
+ */
+
+#include <fcntl.h>
+#include "igt.h"
+#include "amd_mem_leak.h"
+
+
+enum mem_leak_cmd {
+	CMD_SCAN = 0, /* as index */
+	CMD_CLEAR = 1,
+	CMD_MAX,
+};
+
+/* return non zero fp write successfully or null if failure */
+static
+FILE *mem_leak_cmd(enum mem_leak_cmd cmd)
+{
+	const struct mem_leak_cmd_arr {
+		const char *str_cmd;
+		enum mem_leak_cmd cmd;
+	} memleak_arr[] = {
+		{"scan",	CMD_SCAN	},
+		{"clear",	CMD_CLEAR	},
+		{"",		CMD_MAX		},
+		{NULL, 0}
+	};
+
+	FILE *fp;
+	int len;
+
+	fp = fopen("/sys/kernel/debug/kmemleak", "r+");
+	if (fp) {
+		len = strlen(memleak_arr[cmd].str_cmd);
+		if (fwrite(memleak_arr[cmd].str_cmd, 1, len, fp) != len) {
+			fclose(fp);
+			fp = NULL;
+		}
+	}
+
+	return fp;
+}
+
+/* return True if scan successfully written to kmemleak */
+static
+bool send_scan_memleak(void)
+{
+	FILE *fp;
+
+	fp = mem_leak_cmd(CMD_SCAN);
+	if (fp != NULL) {
+		fclose(fp);
+		return true;
+	}
+	return false;
+}
+
+/* return True if clear successfully sent to kmemleak */
+static
+bool send_clear_memleak(void)
+{
+	FILE *fp;
+
+	fp = mem_leak_cmd(CMD_CLEAR);
+	if (fp != NULL) {
+		fclose(fp);
+		return true;
+	}
+	return false;
+}
+
+/* return true if kmemleak is enabled and then clear earlier leak records */
+bool clear_memleak(bool is_more_than_one)
+{
+	if (!send_scan_memleak() || !send_clear_memleak())
+		return false;
+
+	if (is_more_than_one == true) {
+		if (!send_scan_memleak() || !send_clear_memleak())
+			return false;
+	}
+
+	return true;
+}
+
+/* return true if kmemleak did not pick up any memory leaks */
+bool is_no_memleak(void)
+{
+	FILE *fp;
+	const char *buf[1];
+	char read_buf[1024];
+
+	fp = mem_leak_cmd(CMD_SCAN);
+	if (fp != NULL) {
+		/* read back to see if any leak */
+		if (fread(buf, 1, 1, fp) == 0) {
+			fclose(fp);
+			return true;
+		}
+	}
+
+	/* Dump contents of kmemleak */
+	fseek(fp, 0L, SEEK_SET);
+	while (fgets(read_buf, sizeof(read_buf) - 1, fp) != NULL)
+		igt_info("MEM_LEAK: %s", read_buf);
+
+	fclose(fp);
+	return false;
+}
diff --git a/lib/amdgpu/amd_mem_leak.h b/lib/amdgpu/amd_mem_leak.h
new file mode 100644
index 000000000..5de4045a9
--- /dev/null
+++ b/lib/amdgpu/amd_mem_leak.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: MIT
+ * Copyright 2025 Advanced Micro Devices, Inc.
+ */
+#ifndef AMD_MEM_LEAK_H
+#define AMD_MEM_LEAK_H
+
+#include <stdio.h>
+#include <amdgpu.h>
+#include "amd_ip_blocks.h"
+
+/* return true if kmemleak is enabled and then clear earlier leak records */
+bool clear_memleak(bool is_more_than_one);
+
+/* return true if kmemleak did not pick up any memory leaks */
+bool is_no_memleak(void);
+
+#endif
diff --git a/lib/meson.build b/lib/meson.build
index 9fffdd3c6..d01c90df9 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -164,6 +164,7 @@ if libdrm_amdgpu.found()
 		'amdgpu/amd_shared_process.c',
 		'amdgpu/xalloc.h',
 		'amdgpu/amd_cp_dma.c',
+		'amdgpu/amd_mem_leak.c',
 		'amdgpu/amd_mmd_shared.c'
 	]
 	if libdrm_amdgpu.version().version_compare('> 2.4.99')
diff --git a/tests/amdgpu/amd_mem_leak.c b/tests/amdgpu/amd_mem_leak.c
index e4a4b5c47..74f887bf9 100644
--- a/tests/amdgpu/amd_mem_leak.c
+++ b/tests/amdgpu/amd_mem_leak.c
@@ -23,6 +23,7 @@
 #include "igt.h"
 #include "igt_amd.h"
 #include <fcntl.h>
+#include "lib/amdgpu/amd_mem_leak.h"
 
 IGT_TEST_DESCRIPTION("Test checking memory leaks with suspend-resume and connector hotplug");
 
@@ -78,85 +79,6 @@ static void test_fini(data_t *data)
 	igt_display_reset(&data->display);
 }
 
-/* return True if scan successfully written to kmemleak */
-static bool send_scan_memleak(void)
-{
-	FILE *fp;
-	const char *cmd = "scan";
-
-	fp = fopen("/sys/kernel/debug/kmemleak", "r+");
-	if (!fp) return false;
-
-	if(fwrite(cmd, 1, strlen(cmd), fp) != strlen(cmd))  {
-		fclose(fp);
-		return false;
-	}
-	fclose(fp);
-	return true;
-}
-
-/* return True if clear successfully sent to kmemleak */
-static bool send_clear_memleak(void)
-{
-	FILE *fp;
-	const char *cmd = "clear";
-
-	fp = fopen("/sys/kernel/debug/kmemleak", "r+");
-	if (!fp) return false;
-
-	if(fwrite(cmd, 1, strlen(cmd), fp) != strlen(cmd))  {
-		fclose(fp);
-		return false;
-	}
-	fclose(fp);
-	return true;
-}
-
-/* return true if kmemleak is enabled and then clear earlier leak records */
-static bool clear_memleak(data_t *data)
-{
-	/* Need to scan + clear twice to properly clear buffer or else leaks
-	 * from modprobe or other tests may appear
-	 */
-	if (!send_scan_memleak() | !send_clear_memleak())
-		return false;
-	if (!send_scan_memleak() | !send_clear_memleak())
-		return false;
-
-	return true;
-}
-
-/* return true if kmemleak did not pick up any memory leaks */
-static bool check_memleak(data_t *data)
-{
-	FILE *fp;
-	const char *buf[1];
-	const char *cmd = "scan";
-	char read_buf[1024];
-
-	fp = fopen("/sys/kernel/debug/kmemleak", "r+");
-	igt_assert_f(fp, "cannot open /sys/kernel/debug/kmemleak for reading\n");
-
-	/* trigger an immediate scan on memory leak */
-	igt_assert_f(fwrite(cmd, 1, strlen(cmd), fp) == strlen(cmd),
-			"fail to trigger a scan for memory leak\n");
-
-	/* read back to see if any leak */
-	if (fread(buf, 1, 1, fp) == 0) {
-		fclose(fp);
-		return true;
-	}
-
-	/* Dump contents of kmemleak */
-	fseek(fp, 0L, SEEK_SET);
-	while (fgets(read_buf, sizeof(read_buf), fp) != NULL) {
-		igt_info("%s", read_buf);
-	}
-
-	fclose(fp);
-	return false;
-}
-
 static void test_suspend_resume(data_t *data)
 {
 	igt_display_t *display = &data->display;
@@ -164,7 +86,7 @@ static void test_suspend_resume(data_t *data)
 
 	test_init(data);
 
-	if(!clear_memleak(data)) {
+	if(!clear_memleak(true)) {
 		igt_skip("kmemleak is not enabled for this kernel\n");
 	}
 
@@ -174,7 +96,7 @@ static void test_suspend_resume(data_t *data)
 
 	igt_system_suspend_autoresume(SUSPEND_STATE_MEM, SUSPEND_TEST_NONE);
 
-	igt_assert_f(check_memleak(data), "memory leak detected\n");
+	igt_assert_f(is_no_memleak(), "memory leak detected\n");
 
 	igt_remove_fb(data->fd, &rfb);
 	test_fini(data);
@@ -189,7 +111,7 @@ static void test_hotplug(data_t *data)
 
 	igt_amd_require_hpd(&data->display, data->fd);
 
-	if(!clear_memleak(data)) {
+	if(!clear_memleak(true)) {
 		igt_skip("kmemleak is not enabled for this kernel\n");
 	}
 
@@ -199,7 +121,7 @@ static void test_hotplug(data_t *data)
 
 	igt_amd_trigger_hotplug(data->fd, data->output->name);
 
-	igt_assert_f(check_memleak(data), "memory leak detected\n");
+	igt_assert_f(is_no_memleak(), "memory leak detected\n");
 
 	igt_remove_fb(data->fd, &rfb);
 	test_fini(data);
-- 
2.34.1



More information about the igt-dev mailing list