diff --git a/LICENSE b/LICENSE index fc48fd28904f6680c1da24e70d27c7ffb7a31c16..97c58d4662c37e15a56f149eedfb98c00e604414 100644 --- a/LICENSE +++ b/LICENSE @@ -2,5 +2,6 @@ ./newip/ ./xpm/ ./qos_auth/ + ./ucollection/ As for the specific use of the licenses, please refer to the relevant description in the documents. diff --git a/OAT.xml b/OAT.xml index 9de7aacb31ad265d296ee9139b08fe3783166732..55a93aaac5d9767f9e9f8ec15add42c0fa350918 100644 --- a/OAT.xml +++ b/OAT.xml @@ -60,9 +60,11 @@ Note:If the text contains special characters, please escape them according to th + + @@ -81,6 +83,7 @@ Note:If the text contains special characters, please escape them according to th + diff --git a/ucollection/Kconfig b/ucollection/Kconfig new file mode 100644 index 0000000000000000000000000000000000000000..d60e708a1d6a863f59a97ebb4939953e409c2b4b --- /dev/null +++ b/ucollection/Kconfig @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 +config UNIFIED_COLLECTION + def_bool $(success,$(srctree)/scripts/ohos-check-dir.sh $(srctree)/drivers/staging/ucollection) + tristate "Enable unified collection" + help + unified collection \ No newline at end of file diff --git a/ucollection/Makefile b/ucollection/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..5d1b45ebc9fd3a1ed5b9ca39a1136acd718b5b30 --- /dev/null +++ b/ucollection/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_UNIFIED_COLLECTION) += ucollection_process_cpu.o +obj-$(CONFIG_UNIFIED_COLLECTION) += unified_collection_driver.o \ No newline at end of file diff --git a/ucollection/README_zh.md b/ucollection/README_zh.md new file mode 100644 index 0000000000000000000000000000000000000000..3b94e8e0c81b8bba1374928e72fce54666dce584 --- /dev/null +++ b/ucollection/README_zh.md @@ -0,0 +1,32 @@ +## 背景 +当前系统需要获取进程的CPU维测数据,支持开发者性能问题分析,通过遍例所有进程的结点虽然可以获取所有进程的CPU使用率,但是该方法性能相对较差,为了提升获取效率,因此开僻了内核的设备结点,通过结点可能快速获取CPU的数据。 + +## ucollection(unified collection)模块 +通过ioctl的方式向设备结点/dev/uncollection发送命令,内核根据不同的命令做不同的处理,返回相应的维测数据。 +![framework](figures/collector_framework.png) + +## 目录 +统一采集设备的主要代码目录结构如下: + +``` +# 代码路径 /kernel/linux/common_modules/xpm +├── apply_ucollection.sh # XPM 头文件 +├── figures # ReadMe 内嵌图例 +├── Konfig +├── Makefile +├── README_zh.md +├── ucollection_process_cpu.c # 获取进程CPU数据 +├── ucollection_process_cpu.h +├── unified_collection_data.h +├── unified_collection_driver.c # 注册/dev/ucollection设备 +``` + +## 相关仓 + +[内核子系统](https://gitee.com/openharmony/docs/blob/master/zh-cn/readme/%E5%86%85%E6%A0%B8%E5%AD%90%E7%B3%BB%E7%BB%9F.md) + +[kernel_linux_5.10](https://gitee.com/openharmony/kernel_linux_5.10) + +[kernel_linux_config](https://gitee.com/openharmony/kernel_linux_config) + +[kernel_linux_build](https://gitee.com/openharmony/kernel_linux_build) diff --git a/ucollection/apply_ucollection.sh b/ucollection/apply_ucollection.sh new file mode 100755 index 0000000000000000000000000000000000000000..96af21c461d6d560c0c56c9b498c14cfae4db09e --- /dev/null +++ b/ucollection/apply_ucollection.sh @@ -0,0 +1,31 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2023 Huawei Device Co., Ltd. +# +# Description: Create a symbolic link for Unified Collection Driver in Linux 5.10 +# + +set -e + +OHOS_SOURCE_ROOT=$1 +KERNEL_BUILD_ROOT=$2 +PRODUCT_NAME=$3 +KERNEL_VERSION=$4 +UNIFIED_COLLECTION_SOURCE_ROOT=$OHOS_SOURCE_ROOT/kernel/linux/common_modules/ucollection + +function main() +{ + pushd . + + echo "create link $KERNEL_BUILD_ROOT/drivers/staging/ucollection/" + if [ ! -d "$KERNEL_BUILD_ROOT/drivers/staging/ucollection" ]; then + mkdir $KERNEL_BUILD_ROOT/drivers/staging/ucollection + fi + + cd $KERNEL_BUILD_ROOT/drivers/staging/ucollection/ + ln -s -f $(realpath --relative-to=$KERNEL_BUILD_ROOT/drivers/staging/ucollection/ $UNIFIED_COLLECTION_SOURCE_ROOT)/* ./ + + popd +} + +main diff --git a/ucollection/figures/collector_framework.png b/ucollection/figures/collector_framework.png new file mode 100644 index 0000000000000000000000000000000000000000..b56e1ac3d383eaca42505e38f48e7bbe01215ad9 Binary files /dev/null and b/ucollection/figures/collector_framework.png differ diff --git a/ucollection/ucollection_process_cpu.c b/ucollection/ucollection_process_cpu.c new file mode 100644 index 0000000000000000000000000000000000000000..bba8422b4e5a3ea8119dcbebf9dea2352a265b43 --- /dev/null +++ b/ucollection/ucollection_process_cpu.c @@ -0,0 +1,194 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + */ +#include "ucollection_process_cpu.h" + +#include +#ifdef CONFIG_CPU_FREQ_TIMES +#include +#endif // CONFIG_CPU_FREQ_TIMES +#include +#include +#include +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)) +#include +#include +#include +#endif // LINUX_VERSION_CODE +#ifdef CONFIG_SMT_MODE_GOV +#include +#endif // CONFIG_SMT_MODE_GOV + +#include "unified_collection_data.h" + +#define NS_TO_MS 1000000 +static char dmips_values[DMIPS_NUM]; + +unsigned long long __attribute__((weak)) get_proc_cpu_load(struct task_struct *task, char dmips[], + unsigned int dmips_num) +{ + return 0; +} + +static int get_cpu_num(void) +{ + int core_num = 0; + int i = 0; + for_each_possible_cpu(i) + core_num++; + return core_num; +} + +static unsigned long long get_process_load_cputime(struct task_struct *task) +{ + unsigned long long proc_load_cputime = 0; + proc_load_cputime = get_proc_cpu_load(task, dmips_values, DMIPS_NUM); + return proc_load_cputime; +} + +static void get_process_usage_cputime(struct task_struct *task, unsigned long long *ut, unsigned long long *st) +{ + unsigned long long utime, stime; + + thread_group_cputime_adjusted(task, &utime, &stime); + utime = utime + task->signal->cutime; + stime = stime + task->signal->cstime; + do_div(utime, NS_TO_MS); + do_div(stime, NS_TO_MS); + *ut = utime; + *st = stime; +} + +static void get_process_load(struct task_struct *task, int cpu_num, int cur_count, + struct ucollection_process_cpu_entry __user *entry) +{ + struct ucollection_process_cpu_item proc_cpu_entry; + memset(&proc_cpu_entry, 0, sizeof(struct ucollection_process_cpu_item)); + proc_cpu_entry.pid = task->pid; + proc_cpu_entry.cpu_load_time = get_process_load_cputime(task); + get_process_usage_cputime(task, &proc_cpu_entry.cpu_usage_utime, &proc_cpu_entry.cpu_usage_stime); + (void)copy_to_user(&entry->datas[cur_count], &proc_cpu_entry, sizeof(struct ucollection_process_cpu_item)); +} + +static long ioctrl_collect_process_cpu(void __user *argp) +{ + int cpu_num = 0; + struct task_struct *task = NULL; + struct ucollection_process_cpu_entry kentry; + struct ucollection_process_cpu_entry __user *entry = argp; + if (entry == NULL) { + pr_err("cpu entry is null"); + return -EINVAL; + } + + memset(&kentry, 0, sizeof(struct ucollection_process_cpu_entry)); + (void)copy_from_user(&kentry, entry, sizeof(struct ucollection_process_cpu_entry)); + + cpu_num = get_cpu_num(); + rcu_read_lock(); + task = &init_task; + for_each_process(task) { + if (task->pid != task->tgid) + continue; + + if (kentry.cur_count >= kentry.total_count) { + pr_err("process over total count"); + break; + } + + get_process_load(task, cpu_num, kentry.cur_count, entry); + kentry.cur_count++; + } + put_user(kentry.cur_count, &entry->cur_count); + rcu_read_unlock(); + return 0; +} + +static bool is_pid_alive(int pid) +{ + struct task_struct *task = NULL; + task = pid_task(find_vpid(pid), PIDTYPE_PID); + if (task == NULL) + return false; + + return pid_alive(task); +} + +static long ioctrl_collect_the_process_cpu(void __user *argp) +{ + int cpu_num = 0; + struct task_struct *task = NULL; + struct ucollection_process_cpu_entry kentry; + struct ucollection_process_cpu_entry __user *entry = argp; + if (entry == NULL) { + pr_err("cpu entry is null"); + return -EINVAL; + } + + memset(&kentry, 0, sizeof(struct ucollection_process_cpu_entry)); + (void)copy_from_user(&kentry, entry, sizeof(struct ucollection_process_cpu_entry)); + + if (kentry.cur_count >= kentry.total_count) { + pr_err("current count over total count"); + return -EINVAL; + } + + rcu_read_lock(); + if (!is_pid_alive(kentry.filter.pid)) { + pr_err("pid=%d is not alive", kentry.filter.pid); + rcu_read_unlock(); + return -EINVAL; + } + + task = find_task_by_vpid(kentry.filter.pid); + if (task == NULL) { + pr_err("can not get pid=%d", task->pid); + rcu_read_unlock(); + return -EINVAL; + } + + cpu_num = get_cpu_num(); + get_process_load(task, cpu_num, kentry.cur_count, entry); + kentry.cur_count++; + put_user(kentry.cur_count, &entry->cur_count); + rcu_read_unlock(); + return 0; +} + +static long ioctrl_set_cpu_dmips(void __user *argp) +{ + int i; + struct ucollection_cpu_dmips kentry; + struct ucollection_cpu_dmips __user *entry = argp; + memset(&kentry, 0, sizeof(struct ucollection_cpu_dmips)); + (void)copy_from_user(&kentry, entry, sizeof(struct ucollection_cpu_dmips)); + pr_info("set dimps %d cpus\n", kentry.total_count); + for (i = 0; i < DMIPS_NUM; i++) { + if (i >= kentry.total_count) + break; + get_user(dmips_values[i], &entry->dmips[i]); + pr_info("set dimps cpu[%d]=%d\n", i, dmips_values[i]); + } + return 0; +} + +long unified_collection_collect_process_cpu(unsigned int cmd, void __user *argp) +{ + long ret = 0; + switch(cmd) { + case IOCTRL_COLLECT_ALL_PROC_CPU: + ret = ioctrl_collect_process_cpu(argp); + break; + case IOCTRL_COLLECT_THE_PROC_CPU: + ret = ioctrl_collect_the_process_cpu(argp); + break; + case IOCTRL_SET_CPU_DMIPS: + ret = ioctrl_set_cpu_dmips(argp); + break; + default: + pr_err("handle ioctrl cmd %u, _IOC_TYPE(cmd)=%d", cmd, _IOC_TYPE(cmd)); + ret = 0; + } + return ret; +} \ No newline at end of file diff --git a/ucollection/ucollection_process_cpu.h b/ucollection/ucollection_process_cpu.h new file mode 100644 index 0000000000000000000000000000000000000000..09076aa3eea7b4512ccfec2a7f964a1c0fb62e26 --- /dev/null +++ b/ucollection/ucollection_process_cpu.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + */ +#ifndef __UCOLLECTION_PROCESS_CPU__ +#define __UCOLLECTION_PROCESS_CPU__ + +long unified_collection_collect_process_cpu(unsigned int cmd, void __user *argp); +#endif // __UCOLLECTION_PROCESS_CPU__ diff --git a/ucollection/unified_collection_data.h b/ucollection/unified_collection_data.h new file mode 100644 index 0000000000000000000000000000000000000000..ddd2050d3a979e5660b3357d5d25aef493b24684 --- /dev/null +++ b/ucollection/unified_collection_data.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + */ +#ifndef __UNIFIED_COLLECTION_DATA__ +#define __UNIFIED_COLLECTION_DATA__ + +#include + +// kernel struct, modify at the same time +struct ucollection_process_cpu_item { + int pid; + unsigned long long cpu_usage_utime; + unsigned long long cpu_usage_stime; + unsigned long long cpu_load_time; +}; + +struct ucollection_process_filter { + int uid; + int pid; + int tid; +}; + +struct ucollection_process_cpu_entry { + int magic; + int total_count; + int cur_count; + struct ucollection_process_filter filter; + struct ucollection_process_cpu_item datas[]; +}; + +struct ucollection_cpu_dmips { + int magic; + int total_count; + char dmips[]; +}; + +#define IOCTRL_COLLECT_ALL_PROC_CPU_MAGIC 1 +#define IOCTRL_COLLECT_THE_PROC_CPU_MAGIC 1 +#define IOCTRL_SET_CPU_DMIPS_MAGIC 1 +#define DMIPS_NUM 128 + +#define IOCTRL_COLLECT_CPU_BASE 0 +#define IOCTRL_COLLECT_ALL_PROC_CPU _IOR(IOCTRL_COLLECT_CPU_BASE, 1, struct ucollection_process_cpu_entry) +#define IOCTRL_COLLECT_THE_PROC_CPU _IOR(IOCTRL_COLLECT_CPU_BASE, 2, struct ucollection_process_cpu_entry) +#define IOCTRL_SET_CPU_DMIPS _IOW(IOCTRL_COLLECT_CPU_BASE, 3, struct ucollection_cpu_dmips) +#endif // __UNIFIED_COLLECTION_DATA__ \ No newline at end of file diff --git a/ucollection/unified_collection_driver.c b/ucollection/unified_collection_driver.c new file mode 100644 index 0000000000000000000000000000000000000000..be70a171aaf3de1b46f0793ec75566b1fc70ffc8 --- /dev/null +++ b/ucollection/unified_collection_driver.c @@ -0,0 +1,90 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + */ +#include +#include +#include +#include + +#ifdef CONFIG_COMPAT +#include +#endif // CONFIG_COMPAT + +#include "ucollection_process_cpu.h" + +static long (*unified_collection_ioctl_cb[])(unsigned int cmd, void __user *argp) = { + unified_collection_collect_process_cpu /* IOCTRL_COLLECT_CPU */ +}; + +static long unified_collection_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + void __user *argp = u64_to_user_ptr(arg); + const char *comm = NULL; + + if ((_IOC_TYPE(cmd) >= ARRAY_SIZE(unified_collection_ioctl_cb)) || + (unified_collection_ioctl_cb[_IOC_TYPE(cmd)] == NULL)) { + pr_err("invalid ioctrl cmd %u, _IOC_TYPE(cmd)=%d", cmd, _IOC_TYPE(cmd)); + return -EINVAL; + } + + return unified_collection_ioctl_cb[_IOC_TYPE(cmd)](cmd, argp); +} + +#ifdef CONFIG_COMPAT +static long unified_collection_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + return unified_collection_ioctl(filp, cmd, (unsigned long) compat_ptr(arg)); +} +#endif // CONFIG_COMPAT + +static int unified_collection_open(struct inode *inode, struct file *filp) +{ + return 0; +} + +static int unified_collection_release(struct inode *inode, struct file *filp) +{ + return 0; +} + +static const struct file_operations unified_collection_device_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = unified_collection_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = unified_collection_compat_ioctl, +#endif // CONFIG_COMPAT + .open = unified_collection_open, + .release = unified_collection_release, +}; + +static struct miscdevice unified_collection_device = { + .name = "ucollection", + .fops = &unified_collection_device_fops, + .minor = MISC_DYNAMIC_MINOR, +}; + +static int __init unified_collection_init(void) +{ + int ret = misc_register(&unified_collection_device); + if (ret) { + pr_err("failed to register unified collection device"); + return ret; + } + + pr_info("register unified collection device successful"); + return 0; +} + +static void __exit unified_collection_exit(void) +{ + pr_info("deregister unified collection device successful"); + misc_deregister(&unified_collection_device); +} + +module_init(unified_collection_init); +module_exit(unified_collection_exit); + +MODULE_AUTHOR("OHOS"); +MODULE_DESCRIPTION("Unified Collection Driver"); +MODULE_LICENSE("GPL"); \ No newline at end of file