From cc64606a535edd9fd96487631f8ef583226fc575 Mon Sep 17 00:00:00 2001 From: Sunny He Date: Wed, 21 Jun 2017 12:08:08 -0700 Subject: gpu: nvgpu: debugfs code to dump HAL functions Prints addresses of device-specific HAL functions to debugfs file hal/gops. The list of functions is produced by dumping the contents of the gpu_ops substruct of the gk20a struct. This interface makes the assumption that there are only function pointers in gpu_ops. Companion Python script nvgpu_debug_hal.py analyzes gk20a.h to determine operation counts and prettyify debugfs interface's output. Jira NVGPU-107 Change-Id: I0910e86638d144979e8630bbc5b330bccfd3ad94 Signed-off-by: Sunny He Reviewed-on: https://git-master.nvidia.com/r/1542990 Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/gpu/nvgpu/Makefile.nvgpu | 1 + drivers/gpu/nvgpu/common/linux/debug.c | 2 + drivers/gpu/nvgpu/common/linux/debug_hal.c | 95 ++++++++++++++++++++++ drivers/gpu/nvgpu/common/linux/debug_hal.h | 22 ++++++ drivers/gpu/nvgpu/common/linux/os_linux.h | 1 + drivers/gpu/nvgpu/gk20a/gk20a.h | 5 ++ scripts/nvgpu_debug_hal.py | 123 +++++++++++++++++++++++++++++ 7 files changed, 249 insertions(+) create mode 100644 drivers/gpu/nvgpu/common/linux/debug_hal.c create mode 100644 drivers/gpu/nvgpu/common/linux/debug_hal.h create mode 100755 scripts/nvgpu_debug_hal.py diff --git a/drivers/gpu/nvgpu/Makefile.nvgpu b/drivers/gpu/nvgpu/Makefile.nvgpu index 2aa76497..dab9db92 100644 --- a/drivers/gpu/nvgpu/Makefile.nvgpu +++ b/drivers/gpu/nvgpu/Makefile.nvgpu @@ -129,6 +129,7 @@ nvgpu-$(CONFIG_DEBUG_FS) += \ common/linux/debug_sched.o \ common/linux/debug_mm.o \ common/linux/debug_allocator.o \ + common/linux/debug_hal.o \ common/linux/debug_kmem.o \ common/linux/debug_clk.o diff --git a/drivers/gpu/nvgpu/common/linux/debug.c b/drivers/gpu/nvgpu/common/linux/debug.c index 7dce74d6..abc8b907 100644 --- a/drivers/gpu/nvgpu/common/linux/debug.c +++ b/drivers/gpu/nvgpu/common/linux/debug.c @@ -21,6 +21,7 @@ #include "debug_kmem.h" #include "debug_pmu.h" #include "debug_sched.h" +#include "debug_hal.h" #include "os_linux.h" #include "gk20a/gk20a.h" @@ -386,6 +387,7 @@ void gk20a_debug_init(struct gk20a *g, const char *debugfs_symlink) gk20a_cde_debugfs_init(g); gk20a_ce_debugfs_init(g); nvgpu_alloc_debugfs_init(g); + nvgpu_hal_debugfs_init(g); gk20a_mm_debugfs_init(g); gk20a_fifo_debugfs_init(g); gk20a_sched_debugfs_init(g); diff --git a/drivers/gpu/nvgpu/common/linux/debug_hal.c b/drivers/gpu/nvgpu/common/linux/debug_hal.c new file mode 100644 index 00000000..031e335e --- /dev/null +++ b/drivers/gpu/nvgpu/common/linux/debug_hal.c @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2017 NVIDIA Corporation. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 "debug_hal.h" +#include "os_linux.h" + +#include +#include + +/* Format and print a single function pointer to the specified seq_file. */ +static void __hal_print_op(struct seq_file *s, void *op_ptr) +{ + seq_printf(s, "%pF\n", op_ptr); +} + +/* + * Prints an array of function pointer addresses in op_ptrs to the + * specified seq_file + */ +static void __hal_print_ops(struct seq_file *s, void **op_ptrs, int num_ops) +{ + int i; + + for (i = 0; i < num_ops; i++) + __hal_print_op(s, op_ptrs[i]); +} + +/* + * Show file operation, which generates content of the file once. Prints a list + * of gpu operations as defined by gops and the corresponding function pointer + * destination addresses. Relies on no compiler reordering of struct fields and + * assumption that all members are function pointers. + */ +static int __hal_show(struct seq_file *s, void *unused) +{ + struct gpu_ops *gops = s->private; + + __hal_print_ops(s, (void **)gops, sizeof(*gops) / sizeof(void *)); + + return 0; +} + +static int __hal_open(struct inode *inode, struct file *file) +{ + return single_open(file, __hal_show, inode->i_private); +} + +static const struct file_operations __hal_fops = { + .open = __hal_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +void nvgpu_hal_debugfs_fini(struct gk20a *g) +{ + struct nvgpu_os_linux *l = nvgpu_os_linux_from_gk20a(g); + + if (!(l->debugfs_hal == NULL)) + debugfs_remove_recursive(l->debugfs_hal); +} + +void nvgpu_hal_debugfs_init(struct gk20a *g) +{ + struct dentry *d; + struct nvgpu_os_linux *l = nvgpu_os_linux_from_gk20a(g); + + if (!l->debugfs) + return; + l->debugfs_hal = debugfs_create_dir("hal", l->debugfs); + if (IS_ERR_OR_NULL(l->debugfs_hal)) { + l->debugfs_hal = NULL; + return; + } + + /* Pass along reference to the gpu_ops struct as private data */ + d = debugfs_create_file("gops", S_IRUGO, l->debugfs_hal, + &g->ops, &__hal_fops); + if (!d) { + nvgpu_err(g, "%s: Failed to make debugfs node\n", __func__); + debugfs_remove_recursive(l->debugfs_hal); + return; + } +} diff --git a/drivers/gpu/nvgpu/common/linux/debug_hal.h b/drivers/gpu/nvgpu/common/linux/debug_hal.h new file mode 100644 index 00000000..eee6f234 --- /dev/null +++ b/drivers/gpu/nvgpu/common/linux/debug_hal.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2017 NVIDIA Corporation. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 __NVGPU_DEBUG_HAL_H__ +#define __NVGPU_DEBUG_HAL_H__ + +struct gk20a; +void nvgpu_hal_debugfs_fini(struct gk20a *g); +void nvgpu_hal_debugfs_init(struct gk20a *g); + +#endif /* __NVGPU_DEBUG_HAL_H__ */ diff --git a/drivers/gpu/nvgpu/common/linux/os_linux.h b/drivers/gpu/nvgpu/common/linux/os_linux.h index d7fdfa78..ed8364a9 100644 --- a/drivers/gpu/nvgpu/common/linux/os_linux.h +++ b/drivers/gpu/nvgpu/common/linux/os_linux.h @@ -102,6 +102,7 @@ struct nvgpu_os_linux { struct dentry *debugfs_allocators; struct dentry *debugfs_xve; struct dentry *debugfs_kmem; + struct dentry *debugfs_hal; struct dentry *debugfs_force_preemption_cilp; struct dentry *debugfs_force_preemption_gfxp; diff --git a/drivers/gpu/nvgpu/gk20a/gk20a.h b/drivers/gpu/nvgpu/gk20a/gk20a.h index 15e81291..121dd962 100644 --- a/drivers/gpu/nvgpu/gk20a/gk20a.h +++ b/drivers/gpu/nvgpu/gk20a/gk20a.h @@ -130,6 +130,11 @@ enum gk20a_cbc_op { enum nvgpu_unit; +/* + * gpu_ops should only contain function pointers! Non-function pointer members + * should go in struct gk20a or be implemented with the boolean flag API defined + * in nvgpu/enabled.h + */ struct gpu_ops { struct { int (*determine_L2_size_bytes)(struct gk20a *gk20a); diff --git a/scripts/nvgpu_debug_hal.py b/scripts/nvgpu_debug_hal.py new file mode 100755 index 00000000..a56d66e3 --- /dev/null +++ b/scripts/nvgpu_debug_hal.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope 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. +# +# usage: nvgpu_debug_hal.py [-h] [--csv] [--gk20a GK20A] [gops_filename] +# +# Analyze the HAL debugfs interface's output. With no arguments, prints out +# statistics on the gpu_ops struct based on analysis of gk20a.h +# +# positional arguments: +# gops_filename debugfs interface output file (from /d/gpu.0/hal/gops) +# +# optional arguments: +# -h, --help show this help message and exit +# --csv csv formatted output +# --gk20a GK20A path to gk20a.h + +import argparse +import re +from os import environ + +description_str = ('Analyze the HAL debugfs interface\'s output. ' +'With no arguments, prints out statistics on the gpu_ops struct based on ' +'analysis of gk20a.h') + +parser = argparse.ArgumentParser(description=description_str); +parser.add_argument("--csv", help="csv formatted output", action="store_true"); +parser.add_argument("--gk20a", help="path to gk20a.h"); +parser.add_argument("gops_filename", help="debugfs interface output file (from /d/gpu.0/hal/gops)", nargs='?'); +args = parser.parse_args(); + +if args.gk20a: + gk20a_h_path = args.gk20a +else: + top = environ.get('TOP'); + if top is None: + print("$TOP is undefined, unable to find gk20a.h"); + exit(-1); + gk20a_h_path = top + "/kernel/nvgpu/drivers/gpu/nvgpu/gk20a/gk20a.h" + +def get_function_pointer_name(line): + matches = re.search('.*\(\*(?P\w+)\)\(', line); + if matches is None: + return None + else: + return matches.group("function_name"); + +# Build the list of gpu_ops member function pointers from gk20a.h +non_function_pointer_members = []; +formatted_members = []; +gops_members = dict(); +substruct_names = []; +lone_members = []; +with open(gk20a_h_path) as gk20a_h: + # Skip to start of gpu_ops struct + while gk20a_h.readline() != "struct gpu_ops {\n": + continue; + + line = gk20a_h.readline(); + while line != "};\n": + # If this is a substruct + if re.match('\t+struct.+\{', line): + # Read the contents of the substruct + line = gk20a_h.readline(); + struct_contents = "" + while not re.match("\t*\} (\w+);", line): + struct_contents += line; + line = gk20a_h.readline(); + # Split out the substruct name and the function pointer names + struct_name = re.match("\t*\} (?P\w+);", line).group("struct_name"); + struct_members = re.findall(r".+?\(\s*\*\s*(\w+)\s*\).+?;", struct_contents, flags=re.DOTALL) + + # Store the substruct as an entry + substruct_names.append(struct_name); + gops_members[struct_name] = struct_members; + # Format members + for member in struct_members: + formatted_members.append(struct_name + "." + member); + else: + # Lone members (function pointers or stuff not in a substruct) + match = re.match(".*\(\*(?P\w+)\)\(", line); + if match is not None: + # It's a function pointer, keep track of it + lone_members.append(match.group("function_name")); + formatted_members.append(match.group("function_name")); + else: + # Not a function pointer, may also catch comments etc. + non_function_pointer_members.append(line.strip()); + line = gk20a_h.readline(); +if args.gops_filename: + # Interpret gops file + with open(args.gops_filename) as gops: + i = 0; + # Option for csv output + if args.csv: + format_string = '{0},{1}'; + else: + format_string = '{0:<60} = {1}'; + for line in gops: + print(format_string.format(formatted_members[i], line[:-1])); + i += 1; +else: + # Just print some stats on the gpu_ops struct + total = 0; + print("----- Lone Function Pointers -----"); + print("Count =", len(lone_members)); + total += len(lone_members); + for line in lone_members: + print(line); + print("----- Substruct Counts -----"); + for name in substruct_names: + print(name, "=", len(gops_members[name])); + total += len(gops_members[name]) + print("\n Total =", total); -- cgit v1.2.2