852 lines
21 KiB
C
Executable File
852 lines
21 KiB
C
Executable File
// SPDX-License-Identifier: GPL-2.0
|
|
//
|
|
// Samsung's performance logging
|
|
//
|
|
// Copyright (c) 2017 Samsung Electronics Co., Ltd
|
|
// http://www.samsung.com
|
|
//
|
|
// Binse Park <unsang.park@samsung.com>
|
|
|
|
#define KPERFMON_KERNEL
|
|
#include <linux/ologk.h>
|
|
#undef KPERFMON_KERNEL
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/moduleparam.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/proc_fs.h>
|
|
#include <linux/time.h>
|
|
#include <linux/rtc.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/workqueue.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/sec_debug.h>
|
|
#include <linux/perflog.h>
|
|
#include <linux/mm.h>
|
|
#if !defined(KPERFMON_KMALLOC)
|
|
#include <linux/vmalloc.h>
|
|
#endif
|
|
#include <linux/sched/cputime.h>
|
|
#include <linux/sched/signal.h>
|
|
#include <asm/uaccess.h>
|
|
#include <asm/stacktrace.h>
|
|
|
|
#include "kperfmon.h"
|
|
|
|
#define PROC_NAME "kperfmon"
|
|
#if defined(KPERFMON_KMALLOC)
|
|
#define BUFFER_SIZE (5 * 1024)
|
|
#else
|
|
#define MEM_SZ_3G 0xC0000000
|
|
#define BUFFER_SIZE_2M (2 * 1024 * 1024)
|
|
#define BUFFER_SIZE_5M (5 * 1024 * 1024)
|
|
#define IS_LOW_MEMORY_UNDER_3GB (totalram_pages <= (MEM_SZ_3G >> PAGE_SHIFT))
|
|
#endif
|
|
|
|
#define HEADER_SIZE PERFLOG_HEADER_SIZE
|
|
#define DEBUGGER_SIZE 32
|
|
#define STREAM_SIZE (PERFLOG_BUFF_STR_MAX_SIZE + PERFLOG_HEADER_SIZE)
|
|
#define READ_BUFFER_SIZE (STREAM_SIZE + 100)
|
|
|
|
#define MAX_DEPTH_OF_CALLSTACK 20
|
|
#define MAX_MUTEX_RAWDATA 20
|
|
|
|
#define SIGNAL_35 35
|
|
#define SIGNAL_OLOG 5209
|
|
|
|
#if defined(USE_MONITOR)
|
|
#define MAX_MUTEX_RAWDATA_DIGIT 2
|
|
#define DIGIT_UNIT 100000000
|
|
#endif
|
|
|
|
|
|
struct tRingBuffer buffer = {0, };
|
|
//const struct file_operations;
|
|
|
|
struct t_before_print *before_list_cur_pos;
|
|
static LIST_HEAD(before_print_list);
|
|
|
|
void CreateBuffer(struct tRingBuffer *buffer,
|
|
unsigned long length)
|
|
{
|
|
if (buffer->data != 0)
|
|
return;
|
|
|
|
#if defined(KPERFMON_KMALLOC)
|
|
buffer->data = kmalloc(length + 1, GFP_KERNEL);
|
|
#else
|
|
buffer->data = vmalloc(length + 1);
|
|
#endif
|
|
if (buffer->data == 0) {
|
|
pr_info("kperfmon error [%s] buffer->data is null!!!\n",
|
|
__func__);
|
|
return;
|
|
}
|
|
|
|
buffer->length = length;
|
|
buffer->start = -1;
|
|
buffer->end = 0;
|
|
buffer->status = FLAG_NOTHING;
|
|
buffer->debugger = 0;
|
|
|
|
memset(buffer->data, 0, length + 1);
|
|
|
|
mutex_init(&buffer->mutex);
|
|
}
|
|
|
|
void DestroyBuffer(struct tRingBuffer *buffer)
|
|
{
|
|
if (buffer->data != 0) {
|
|
#if defined(KPERFMON_KMALLOC)
|
|
kfree(buffer->data);
|
|
#else
|
|
vfree(buffer->data);
|
|
#endif
|
|
buffer->data = 0;
|
|
}
|
|
}
|
|
|
|
void WriteBuffer(struct tRingBuffer *buffer,
|
|
byte *data,
|
|
unsigned long length)
|
|
{
|
|
long RemainSize = 0;
|
|
|
|
if (length < 0)
|
|
return;
|
|
|
|
if (buffer->length < buffer->end + length) {
|
|
long FirstSize = buffer->length - buffer->end;
|
|
|
|
WriteBuffer(buffer, data, FirstSize);
|
|
WriteBuffer(buffer, data + FirstSize, length - FirstSize);
|
|
return;
|
|
}
|
|
|
|
RemainSize = (buffer->start < buffer->end) ?
|
|
(buffer->length - buffer->end) :
|
|
(buffer->start - buffer->end);
|
|
|
|
while (RemainSize < length) {
|
|
int bstart = (buffer->start + HEADER_SIZE - 1);
|
|
int cur_length = *(buffer->data + bstart % buffer->length);
|
|
|
|
buffer->start += HEADER_SIZE + cur_length;
|
|
buffer->start %= buffer->length;
|
|
|
|
RemainSize = (buffer->start < buffer->end) ?
|
|
(buffer->length - buffer->end) :
|
|
(buffer->start - buffer->end);
|
|
}
|
|
|
|
memcpy(buffer->data + buffer->end, data, length);
|
|
//copy_from_user(buffer->data + buffer->end, data, length);
|
|
|
|
buffer->end += length;
|
|
|
|
if (buffer->start < 0)
|
|
buffer->start = 0;
|
|
|
|
if (buffer->end >= buffer->length)
|
|
buffer->end = 0;
|
|
|
|
if (buffer->status != FLAG_READING)
|
|
buffer->position = buffer->start;
|
|
}
|
|
|
|
void ReadBuffer(struct tRingBuffer *buffer,
|
|
byte *data,
|
|
unsigned long *length)
|
|
{
|
|
if (buffer->start < buffer->end) {
|
|
*length = buffer->end - buffer->start;
|
|
memcpy(data, buffer->data + buffer->start, *length);
|
|
//copy_to_user(data, (buffer->data + buffer->start), *length);
|
|
} else {
|
|
*length = buffer->length - buffer->start;
|
|
memcpy(data, buffer->data + buffer->start, *length);
|
|
memcpy(data + *length, buffer->data, buffer->end);
|
|
//copy_to_user(data, (buffer->data + buffer->start), *length);
|
|
//copy_to_user(data + *length, (buffer->data), buffer->end);
|
|
}
|
|
}
|
|
|
|
void ReadBufferByPosition(struct tRingBuffer *buffer,
|
|
byte *data,
|
|
unsigned long *length,
|
|
unsigned long start,
|
|
unsigned long end)
|
|
{
|
|
if (start < end) {
|
|
*length = end - start;
|
|
memcpy(data, buffer->data + start, *length);
|
|
} else {
|
|
*length = buffer->length - start;
|
|
memcpy(data, buffer->data + start, *length);
|
|
memcpy(data + *length, buffer->data, end);
|
|
}
|
|
}
|
|
|
|
void GetNext(struct tRingBuffer *buffer)
|
|
{
|
|
int bstart = (buffer->position + HEADER_SIZE - 1);
|
|
int cur_length = *(buffer->data + bstart % buffer->length);
|
|
|
|
buffer->position += HEADER_SIZE + cur_length;
|
|
buffer->position %= buffer->length;
|
|
}
|
|
|
|
static const struct file_operations kperfmon_fops = {
|
|
.read = kperfmon_read,
|
|
.write = kperfmon_write,
|
|
};
|
|
|
|
void set_kperfmon_debugger_function(char *writebuffer)
|
|
{
|
|
buffer.debugger = !buffer.debugger;
|
|
|
|
pr_info("%s() - buffer.debugger : %d\n",
|
|
__func__,
|
|
(int)buffer.debugger);
|
|
}
|
|
|
|
void process_version_function(char *writebuffer)
|
|
{
|
|
struct t_before_print *pprinter = NULL;
|
|
int length = 0;
|
|
|
|
if (writebuffer == NULL)
|
|
return;
|
|
|
|
pprinter = kmalloc(sizeof(struct t_before_print), GFP_ATOMIC);
|
|
|
|
if (pprinter == NULL)
|
|
return;
|
|
|
|
length = strlen(writebuffer) + 1;
|
|
pprinter->pdata = kmalloc(length, GFP_ATOMIC);
|
|
|
|
if (pprinter->pdata == NULL) {
|
|
kfree(pprinter);
|
|
return;
|
|
}
|
|
|
|
strlcpy(pprinter->pdata, writebuffer, length);
|
|
|
|
list_add_tail(&pprinter->list, &before_print_list);
|
|
}
|
|
|
|
int ops_write_buffer(struct tRingBuffer *buffer,
|
|
byte *writebuffer, unsigned long length)
|
|
{
|
|
unsigned long DataLength;
|
|
|
|
if (buffer == NULL)
|
|
return length;
|
|
|
|
if (writebuffer[HEADER_SIZE - 1] > PERFLOG_BUFF_STR_MAX_SIZE)
|
|
writebuffer[HEADER_SIZE - 1] = PERFLOG_BUFF_STR_MAX_SIZE;
|
|
|
|
DataLength = writebuffer[HEADER_SIZE - 1] + HEADER_SIZE;
|
|
|
|
mutex_lock(&buffer->mutex);
|
|
WriteBuffer(buffer, writebuffer, DataLength);
|
|
mutex_unlock(&buffer->mutex);
|
|
#if defined(KPERFMON_DEBUG)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0 ; i < 110 ; i++) {
|
|
pr_info("%s(buffer.data[%d] : %c\n",
|
|
__func__,
|
|
i,
|
|
buffer.data[i]);
|
|
}
|
|
}
|
|
#endif
|
|
return length;
|
|
}
|
|
|
|
int ops_process_command(struct tRingBuffer *buffer,
|
|
byte *writebuffer, unsigned long length)
|
|
{
|
|
int idx;
|
|
int max_commands = (int)(sizeof(commands) / sizeof(struct t_command));
|
|
|
|
for (idx = 0 ; idx < max_commands ; idx++) {
|
|
int cmd_length = strlen(commands[idx].command);
|
|
|
|
if (cmd_length >= HEADER_SIZE + PERFLOG_BUFF_STR_MAX_SIZE)
|
|
continue;
|
|
|
|
if (!strncmp(writebuffer, commands[idx].command, cmd_length)
|
|
&& commands[idx].func != NULL
|
|
&& strlen(writebuffer) > cmd_length) {
|
|
commands[idx].func(writebuffer);
|
|
return length;
|
|
}
|
|
}
|
|
|
|
return length;
|
|
}
|
|
|
|
ssize_t kperfmon_write(struct file *filp,
|
|
const char __user *data,
|
|
size_t length,
|
|
loff_t *loff_data)
|
|
{
|
|
byte writebuffer[HEADER_SIZE + PERFLOG_BUFF_STR_MAX_SIZE + SH_IDX_PACKET + 1] = {0, };
|
|
unsigned long DataLength = length;
|
|
int max_write_ops = (int)(sizeof(write_opts) / sizeof(void *));
|
|
int type_of_data;
|
|
|
|
if (!buffer.data) {
|
|
pr_info("%s() - Error buffer allocation is failed!!!\n",
|
|
__func__);
|
|
return length;
|
|
}
|
|
|
|
if (length <= 0) {
|
|
pr_info("%s() - Error length : %d", __func__, length);
|
|
return length;
|
|
}
|
|
|
|
if (DataLength > (HEADER_SIZE + PERFLOG_BUFF_STR_MAX_SIZE + SH_IDX_PACKET))
|
|
DataLength = HEADER_SIZE + PERFLOG_BUFF_STR_MAX_SIZE + SH_IDX_PACKET;
|
|
|
|
if (copy_from_user(writebuffer, data, DataLength))
|
|
return length;
|
|
|
|
// [[[ This will be replaced with below code
|
|
type_of_data = writebuffer[SH_TYPE];
|
|
|
|
if (type_of_data < max_write_ops && type_of_data >= 0)
|
|
return write_opts[type_of_data](&buffer,
|
|
writebuffer + SH_IDX_PACKET,
|
|
DataLength - SH_IDX_PACKET);
|
|
// This will be replaced with below code ]]]
|
|
|
|
type_of_data -= (int)'0';
|
|
|
|
if (type_of_data < max_write_ops && type_of_data >= 0)
|
|
return write_opts[type_of_data](&buffer,
|
|
writebuffer + SH_IDX_PACKET,
|
|
DataLength - SH_IDX_PACKET);
|
|
|
|
return length;
|
|
}
|
|
|
|
ssize_t kperfmon_read(struct file *filp,
|
|
char __user *data,
|
|
size_t count,
|
|
loff_t *loff_data)
|
|
{
|
|
unsigned long length;
|
|
byte readbuffer[READ_BUFFER_SIZE] = {0, };
|
|
union _uPLogPacket readlogpacket;
|
|
char timestamp[32] = {0, };
|
|
|
|
unsigned long start = 0;
|
|
unsigned long end = 0;
|
|
|
|
if (!buffer.data) {
|
|
pr_info("%s() Error buffer allocation is failed!\n", __func__);
|
|
return 0;
|
|
}
|
|
#if defined(USE_MONITOR)
|
|
if (buffer.position == buffer.start) {
|
|
char mutex_log[PERFLOG_BUFF_STR_MAX_SIZE + 1] = {0, };
|
|
int i, idx_mutex_log = 0;
|
|
|
|
idx_mutex_log += snprintf((mutex_log + idx_mutex_log),
|
|
PERFLOG_BUFF_STR_MAX_SIZE - idx_mutex_log,
|
|
"mutex test ");
|
|
|
|
for (i = 0;
|
|
i <= MAX_MUTEX_RAWDATA &&
|
|
idx_mutex_log < (PERFLOG_BUFF_STR_MAX_SIZE - 20);
|
|
i++) {
|
|
|
|
int digit, flag = 0;
|
|
|
|
mutex_log[idx_mutex_log++] = '[';
|
|
idx_mutex_log += snprintf((mutex_log + idx_mutex_log),
|
|
PERFLOG_BUFF_STR_MAX_SIZE - idx_mutex_log,
|
|
"%d",
|
|
i);
|
|
mutex_log[idx_mutex_log++] = ']';
|
|
mutex_log[idx_mutex_log++] = ':';
|
|
//idx_mutex_log += snprintf((mutex_log + idx_mutex_log),
|
|
// PERFLOG_BUFF_STR_MAX_SIZE - idx_mutex_log,
|
|
// "%d",
|
|
// mutex_rawdata[i]);
|
|
//mutex_rawdata[i][1] = 99999999;
|
|
for (digit = (MAX_MUTEX_RAWDATA_DIGIT-1) ; digit >= 0 ; digit--) {
|
|
if (flag) {
|
|
idx_mutex_log += snprintf((mutex_log + idx_mutex_log),
|
|
PERFLOG_BUFF_STR_MAX_SIZE - idx_mutex_log,
|
|
"%08u",
|
|
mutex_rawdata[i][digit]);
|
|
} else {
|
|
if (mutex_rawdata[i][digit] > 0) {
|
|
idx_mutex_log += snprintf((mutex_log + idx_mutex_log),
|
|
PERFLOG_BUFF_STR_MAX_SIZE - idx_mutex_log,
|
|
"%u",
|
|
mutex_rawdata[i][digit]);
|
|
flag = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!flag)
|
|
mutex_log[idx_mutex_log++] = '0';
|
|
|
|
mutex_log[idx_mutex_log++] = ' ';
|
|
}
|
|
|
|
_perflog(PERFLOG_EVT, PERFLOG_MUTEX, mutex_log);
|
|
}
|
|
#endif
|
|
buffer.status = FLAG_READING;
|
|
|
|
mutex_lock(&buffer.mutex);
|
|
|
|
if (buffer.position == buffer.start) {
|
|
|
|
if (before_list_cur_pos !=
|
|
list_last_entry(&before_print_list, typeof(*before_list_cur_pos), list)) {
|
|
before_list_cur_pos = list_next_entry(before_list_cur_pos, list);
|
|
|
|
if (before_list_cur_pos != 0 && before_list_cur_pos->pdata != 0) {
|
|
int length = snprintf(readbuffer,
|
|
READ_BUFFER_SIZE,
|
|
"%s\n",
|
|
before_list_cur_pos->pdata);
|
|
|
|
if (copy_to_user(data, readbuffer, length)) {
|
|
pr_info("%s(copy_to_user(4) returned > 0)\n", __func__);
|
|
mutex_unlock(&buffer.mutex);
|
|
buffer.status = FLAG_NOTHING;
|
|
return 0;
|
|
}
|
|
|
|
mutex_unlock(&buffer.mutex);
|
|
return length;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (buffer.position == buffer.end || buffer.start < 0) {
|
|
buffer.position = buffer.start;
|
|
mutex_unlock(&buffer.mutex);
|
|
buffer.status = FLAG_NOTHING;
|
|
before_list_cur_pos
|
|
= list_first_entry(&before_print_list, typeof(*before_list_cur_pos), list);
|
|
return 0;
|
|
}
|
|
|
|
start = buffer.position;
|
|
GetNext(&buffer);
|
|
end = buffer.position;
|
|
|
|
//printk("kperfmon_read(start : %d, end : %d)\n", (int)start, (int)end);
|
|
|
|
if (start == end) {
|
|
buffer.position = buffer.start;
|
|
mutex_unlock(&buffer.mutex);
|
|
buffer.status = FLAG_NOTHING;
|
|
return 0;
|
|
}
|
|
|
|
//ReadPacket.raw = &rawpacket;
|
|
ReadBufferByPosition(&buffer, readlogpacket.stream, &length, start, end);
|
|
mutex_unlock(&buffer.mutex);
|
|
//printk(KERN_INFO "kperfmon_read(length : %d)\n", (int)length);
|
|
//readlogpacket.stream[length++] = '\n';
|
|
readlogpacket.stream[length] = 0;
|
|
|
|
#if NOT_USED
|
|
change2localtime(timestamp, readlogpacket.itemes.timestemp_sec);
|
|
#else
|
|
snprintf(timestamp, 32, "%02d-%02d %02d:%02d:%02d.%03d",
|
|
readlogpacket.itemes.timestamp.month,
|
|
readlogpacket.itemes.timestamp.day,
|
|
readlogpacket.itemes.timestamp.hour,
|
|
readlogpacket.itemes.timestamp.minute,
|
|
readlogpacket.itemes.timestamp.second,
|
|
readlogpacket.itemes.timestamp.msecond);
|
|
#endif
|
|
|
|
if (readlogpacket.itemes.type >= OlogTestEnum_Type_maxnum
|
|
|| readlogpacket.itemes.type < 0) {
|
|
readlogpacket.itemes.type = PERFLOG_LOG;
|
|
}
|
|
|
|
if (readlogpacket.itemes.id >= OlogTestEnum_ID_maxnum
|
|
|| readlogpacket.itemes.id < 0) {
|
|
readlogpacket.itemes.id = PERFLOG_UNKNOWN;
|
|
}
|
|
|
|
length = snprintf(readbuffer, READ_BUFFER_SIZE,
|
|
"[%s %d %5d %5d (%3d)][%s][%s] %s\n",
|
|
timestamp,
|
|
readlogpacket.itemes.type,
|
|
readlogpacket.itemes.pid,
|
|
readlogpacket.itemes.tid,
|
|
readlogpacket.itemes.context_length,
|
|
OlogTestEnum_Type_strings[readlogpacket.itemes.type],
|
|
OlogTestEnum_ID_strings[readlogpacket.itemes.id],
|
|
readlogpacket.itemes.context_buffer);
|
|
|
|
|
|
if (length > count)
|
|
length = count;
|
|
|
|
if (buffer.debugger && count > DEBUGGER_SIZE) {
|
|
char debugger[DEBUGGER_SIZE] = "______________________________";
|
|
|
|
snprintf(debugger, DEBUGGER_SIZE, "S:%010lu_E:%010lu_____", start, end);
|
|
|
|
if (length + DEBUGGER_SIZE > count)
|
|
length = count - DEBUGGER_SIZE;
|
|
|
|
if (copy_to_user(data, debugger, strnlen(debugger, DEBUGGER_SIZE))) {
|
|
pr_info("%s(copy_to_user(1) returned > 0)\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
if (copy_to_user(data + DEBUGGER_SIZE, readbuffer, length)) {
|
|
pr_info("%s(copy_to_user(2) returned > 0)\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
length += DEBUGGER_SIZE;
|
|
} else {
|
|
if (copy_to_user(data, readbuffer, length)) {
|
|
pr_info("%s(copy_to_user(3) returned > 0)\n", __func__);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
//printk(KERN_INFO "kperfmon_read(count : %d)\n", count);
|
|
|
|
|
|
return length;
|
|
}
|
|
|
|
static int __init kperfmon_init(void)
|
|
{
|
|
struct proc_dir_entry *entry;
|
|
|
|
#if defined(KPERFMON_KMALLOC)
|
|
CreateBuffer(&buffer, BUFFER_SIZE);
|
|
#else
|
|
char *context_buffer_size;
|
|
|
|
if (IS_LOW_MEMORY_UNDER_3GB) {
|
|
CreateBuffer(&buffer, BUFFER_SIZE_2M);
|
|
context_buffer_size = "kperfmon buffer size [2M]";
|
|
} else {
|
|
CreateBuffer(&buffer, BUFFER_SIZE_5M);
|
|
context_buffer_size = "kperfmon buffer size [5M]";
|
|
}
|
|
#endif
|
|
|
|
if (!buffer.data) {
|
|
pr_info("%s() - Error buffer allocation is failed!!!\n", __func__);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
entry = proc_create(PROC_NAME, 0664, NULL, &kperfmon_fops);
|
|
|
|
if (!entry) {
|
|
pr_info("%s() - Error creating entry in proc failed!!!\n", __func__);
|
|
DestroyBuffer(&buffer);
|
|
return -EBUSY;
|
|
}
|
|
|
|
/*dbg_level_is_low = (sec_debug_level() == ANDROID_DEBUG_LEVEL_LOW);*/
|
|
|
|
INIT_LIST_HEAD(&before_print_list);
|
|
before_list_cur_pos =
|
|
list_first_entry(&before_print_list, typeof(*before_list_cur_pos), list);
|
|
process_version_function(" ");
|
|
process_version_function("kperfmon_version [1.0]");
|
|
#if !defined(KPERFMON_KMALLOC)
|
|
process_version_function(context_buffer_size);
|
|
#endif
|
|
|
|
pr_info("%s()\n", __func__);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void __exit kperfmon_exit(void)
|
|
{
|
|
DestroyBuffer(&buffer);
|
|
pr_info("%s()\n", __func__);
|
|
}
|
|
|
|
#if defined(USE_WORKQUEUE)
|
|
static void ologk_workqueue_func(struct work_struct *work)
|
|
{
|
|
struct t_ologk_work *workqueue = (struct t_ologk_work *)work;
|
|
|
|
if (work) {
|
|
mutex_lock(&buffer.mutex);
|
|
WriteBuffer(&buffer,
|
|
workqueue->writelogpacket.stream,
|
|
PERFLOG_HEADER_SIZE + workqueue->writelogpacket.itemes.context_length);
|
|
mutex_unlock(&buffer.mutex);
|
|
|
|
kfree((void *)work);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)
|
|
//static inline void do_gettimeofday(struct timeval *tv)
|
|
//{
|
|
// struct timespec64 now;
|
|
|
|
// ktime_get_real_ts64(&now);
|
|
// tv->tv_sec = now.tv_sec;
|
|
// tv->tv_usec = now.tv_nsec/1000;
|
|
//}
|
|
//#endif /* LINUX_VER >= 5.0 */
|
|
|
|
void _perflog(int type, int logid, const char *fmt, ...)
|
|
{
|
|
#if !defined(USE_WORKQUEUE)
|
|
union _uPLogPacket writelogpacket;
|
|
#endif
|
|
struct rtc_time tm;
|
|
struct timeval time;
|
|
unsigned long local_time;
|
|
#if defined(USE_WORKQUEUE)
|
|
struct t_ologk_work *workqueue = 0;
|
|
#endif
|
|
va_list args;
|
|
|
|
va_start(args, fmt);
|
|
|
|
if (buffer.data == 0) {
|
|
va_end(args);
|
|
return;
|
|
}
|
|
|
|
#if defined(USE_WORKQUEUE)
|
|
workqueue = kmalloc(sizeof(struct t_ologk_work), GFP_ATOMIC);
|
|
|
|
if (workqueue) {
|
|
struct _PLogPacket *pitemes = &workqueue->writelogpacket.itemes;
|
|
|
|
INIT_WORK((struct work_struct *)workqueue, ologk_workqueue_func);
|
|
|
|
do_gettimeofday(&time);
|
|
local_time = (u32)(time.tv_sec - (sys_tz.tz_minuteswest * 60));
|
|
rtc_time_to_tm(local_time, &tm);
|
|
|
|
//printk(" @ (%04d-%02d-%02d %02d:%02d:%02d)\n",
|
|
// tm.tm_year + 1900,
|
|
// tm.tm_mon + 1,
|
|
// tm.tm_mday,
|
|
// tm.tm_hour,
|
|
// tm.tm_min,
|
|
// tm.tm_sec);
|
|
|
|
pitemes->timestamp.month = tm.tm_mon + 1;
|
|
pitemes->timestamp.day = tm.tm_mday;
|
|
pitemes->timestamp.hour = tm.tm_hour;
|
|
pitemes->timestamp.minute = tm.tm_min;
|
|
pitemes->timestamp.second = tm.tm_sec;
|
|
pitemes->timestamp.msecond = time.tv_usec / 1000;
|
|
pitemes->type = PERFLOG_LOG;
|
|
pitemes->id = logid;
|
|
pitemes->pid = current->pid;//getpid();
|
|
pitemes->tid = 0;//gettid();
|
|
pitemes->context_length = vscnprintf(
|
|
pitemes->context_buffer,
|
|
PERFLOG_BUFF_STR_MAX_SIZE,
|
|
fmt,
|
|
args);
|
|
|
|
if (pitemes->context_length > PERFLOG_BUFF_STR_MAX_SIZE)
|
|
pitemes->context_length = PERFLOG_BUFF_STR_MAX_SIZE;
|
|
|
|
schedule_work((struct work_struct *)workqueue);
|
|
|
|
//{
|
|
// struct timeval end_time;
|
|
// do_gettimeofday(&end_time);
|
|
// printk("ologk() execution time with workqueue : %ld us ( %ld - %ld )\n",
|
|
// end_time.tv_usec - time.tv_usec,
|
|
// end_time.tv_usec,
|
|
// time.tv_usec);
|
|
//}
|
|
} else {
|
|
pr_info("%s : workqueue is not working\n", __func__);
|
|
}
|
|
|
|
#else
|
|
do_gettimeofday(&time);
|
|
local_time = (u32)(time.tv_sec - (sys_tz.tz_minuteswest * 60));
|
|
rtc_time_to_tm(local_time, &tm);
|
|
|
|
//printk(" @ (%04d-%02d-%02d %02d:%02d:%02d)\n",
|
|
// tm.tm_year + 1900,
|
|
// tm.tm_mon + 1,
|
|
// tm.tm_mday,
|
|
// tm.tm_hour,
|
|
// tm.tm_min,
|
|
// tm.tm_sec);
|
|
|
|
writelogpacket.itemes.timestamp.month = tm.tm_mon + 1;
|
|
writelogpacket.itemes.timestamp.day = tm.tm_mday;
|
|
writelogpacket.itemes.timestamp.hour = tm.tm_hour;
|
|
writelogpacket.itemes.timestamp.minute = tm.tm_min;
|
|
writelogpacket.itemes.timestamp.second = tm.tm_sec;
|
|
writelogpacket.itemes.timestamp.msecond = time.tv_usec / 1000;
|
|
writelogpacket.itemes.type = type;
|
|
writelogpacket.itemes.pid = current->pid;//getpid();
|
|
writelogpacket.itemes.tid = 0;//gettid();
|
|
writelogpacket.itemes.context_length
|
|
= vscnprintf(writelogpacket.itemes.context_buffer,
|
|
PERFLOG_BUFF_STR_MAX_SIZE,
|
|
fmt,
|
|
args);
|
|
|
|
if (writelogpacket.itemes.context_length > PERFLOG_BUFF_STR_MAX_SIZE)
|
|
writelogpacket.itemes.context_length = PERFLOG_BUFF_STR_MAX_SIZE;
|
|
|
|
mutex_lock(&buffer.mutex);
|
|
WriteBuffer(&buffer,
|
|
writelogpacket.stream,
|
|
PERFLOG_HEADER_SIZE + writelogpacket.itemes.context_length);
|
|
mutex_unlock(&buffer.mutex);
|
|
|
|
//{
|
|
// struct timeval end_time;
|
|
// do_gettimeofday(&end_time);
|
|
// printk(KERN_INFO "ologk() execution time : %ld us ( %ld - %ld )\n",
|
|
// end_time.tv_usec - time.tv_usec,
|
|
// end_time.tv_usec, time.tv_usec);
|
|
//}
|
|
#endif
|
|
|
|
va_end(args);
|
|
}
|
|
|
|
void get_callstack(char *buffer, int max_size, int max_count)
|
|
{
|
|
struct stackframe frame;
|
|
struct task_struct *tsk = current;
|
|
//int len;
|
|
|
|
if (!try_get_task_stack(tsk))
|
|
return;
|
|
|
|
frame.fp = (unsigned long)__builtin_frame_address(0);
|
|
frame.pc = (unsigned long)get_callstack;
|
|
|
|
#if defined(CONFIG_FUNCTION_GRAPH_TRACER)
|
|
frame.graph = tsk->curr_ret_stack;
|
|
#endif
|
|
if (max_size > 0) {
|
|
int count = 0;
|
|
|
|
max_count += 3;
|
|
|
|
do {
|
|
if (count > 2) {
|
|
int len = snprintf(buffer, max_size, " %pS", (void *)frame.pc);
|
|
max_size -= len;
|
|
buffer += len;
|
|
}
|
|
count++;
|
|
} while (!unwind_frame(tsk, &frame) &&
|
|
max_size > 0 &&
|
|
max_count > count);
|
|
|
|
put_task_stack(tsk);
|
|
}
|
|
}
|
|
|
|
//void send_signal(void)
|
|
//{
|
|
// siginfo_t info;
|
|
//
|
|
// info.si_signo = SIGNAL_35;
|
|
// info.si_errno = SIGNAL_OLOG;
|
|
// info.si_code = SIGNAL_OLOG;
|
|
// send_sig_info(SIGNAL_35, &info, current);
|
|
//}
|
|
|
|
void perflog_evt(int logid, int arg1)
|
|
{
|
|
#if defined(USE_MONITOR)
|
|
struct timeval start_time;
|
|
struct timeval end_time;
|
|
|
|
int digit = 0;
|
|
|
|
do_gettimeofday(&start_time);
|
|
#endif
|
|
if (arg1 < 0 || buffer.status != FLAG_NOTHING)
|
|
return;
|
|
|
|
if (arg1 > MAX_MUTEX_RAWDATA) {
|
|
char log_buffer[PERFLOG_BUFF_STR_MAX_SIZE];
|
|
int len;
|
|
u64 utime, stime;
|
|
|
|
task_cputime(current, &utime, &stime);
|
|
|
|
if (utime > 0) {
|
|
len = snprintf(log_buffer,
|
|
PERFLOG_BUFF_STR_MAX_SIZE,
|
|
"%d jiffies",
|
|
arg1);
|
|
// Make some stuck problems to be needed to check
|
|
// how many the mutex logging are occurred.
|
|
// Refer to P200523-00343, P200523-01815.
|
|
/*send_signal();*/
|
|
|
|
get_callstack(log_buffer + len,
|
|
PERFLOG_BUFF_STR_MAX_SIZE - len,
|
|
/*(dbg_level_is_low ? 1 : 3)*/MAX_DEPTH_OF_CALLSTACK);
|
|
_perflog(PERFLOG_EVT, PERFLOG_MUTEX, log_buffer);
|
|
arg1 = MAX_MUTEX_RAWDATA;
|
|
|
|
//do_gettimeofday(&end_time);
|
|
//_perflog(PERFLOG_EVT,
|
|
// PERFLOG_MUTEX,
|
|
// "[MUTEX] processing time : %d",
|
|
// end_time.tv_usec - start_time.tv_usec);
|
|
}
|
|
}
|
|
#if defined(USE_MONITOR)
|
|
for (digit = 0 ; digit < MAX_MUTEX_RAWDATA_DIGIT ; digit++) {
|
|
mutex_rawdata[arg1][digit]++;
|
|
if (mutex_rawdata[arg1][digit] >= DIGIT_UNIT)
|
|
mutex_rawdata[arg1][digit] = 0;
|
|
else
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
//EXPORT_SYMBOL(ologk);
|
|
EXPORT_SYMBOL(_perflog);
|
|
EXPORT_SYMBOL(perflog_evt);
|
|
|
|
module_init(kperfmon_init);
|
|
module_exit(kperfmon_exit);
|
|
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_AUTHOR("Binse Park <unsang.park@samsung.com>");
|
|
MODULE_DESCRIPTION("Performance Log(OLOG)");
|
|
|