이번에는 조금 더 재미있는 패치 코드를 같이 볼까요? 다음은 패치 코드의 내용입니다.
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index cd8b61b..128c998 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -49,7 +49,7 @@
#include <linux/uaccess.h>
#include <linux/sched/isolation.h>
#include <linux/nmi.h>
-
+#include <linux/sched_clock.h>
#include "workqueue_internal.h"
enum {
@@ -2029,6 +2029,18 @@ static bool manage_workers(struct worker *worker)
return true;
}
+static int work_timer_debug = 1;
+static unsigned long long max_work_handler_time_stamp = 0;
+
+static void work_handler_elapsed_timer_func(struct timer_list *unused)
+{
+ unsigned long long curr_time = sched_clock();
+
+ pr_err("work_handler max elapsed time : %lld, cur time: %lld\n",
+ max_work_handler_time_stamp, curr_time);
+}
+static DEFINE_TIMER(work_handler_elapsed_timer, work_handler_elapsed_timer_func);
+
/**
* process_one_work - process single work
* @worker: self
@@ -2052,6 +2064,7 @@ __acquires(&pool->lock)
bool cpu_intensive = pwq->wq->flags & WQ_CPU_INTENSIVE;
int work_color;
struct worker *collision;
+ u64 start_time, end_time, elapse_time;
#ifdef CONFIG_LOCKDEP
/*
* It is permissible to free the struct work_struct from
@@ -2149,6 +2162,12 @@ __acquires(&pool->lock)
* workqueues), so hiding them isn't a problem.
*/
lockdep_invariant_state(true);
+
+ if (work_timer_debug) {
+ start_time = sched_clock();
+ mod_timer(&work_handler_elapsed_timer, msecs_to_jiffies(500) + jiffies);
+ }
+
trace_workqueue_execute_start(work);
worker->current_func(work);
/*
@@ -2156,6 +2175,19 @@ __acquires(&pool->lock)
* point will only record its address.
*/
trace_workqueue_execute_end(work);
+
+ if (work_timer_debug) {
+ end_time = sched_clock();
+ del_timer_sync(&work_handler_elapsed_timer);
+ elapse_time = end_time - start_time;
+
+ if (elapse_time > max_work_handler_time_stamp) {
+ max_work_handler_time_stamp = elapse_time;
+ pr_err("work_handler elapsed time : %lld, handler: %pS\n",
+ max_work_handler_time_stamp, worker->current_func);
+ }
+ }
+
lock_map_release(&lockdep_map);
lock_map_release(&pwq->wq->lockdep_map);
@@ -2235,6 +2267,9 @@ static void set_pf_worker(bool val)
*
* Return: 0
*/
위 패치 코드는 2가지 기능을 지원하는데 세부 동작은 두 가지 시나리오에서 생각해볼 수 있습니다.
< 첫 번째 시나리오 >
1. 타이머 설정(500ms)
2. worker->current_func(work); 함수 실행
3. 'worker->current_func(work)' 코드의 실행 시각이 500ms 이내이면
타이머 해제
4. 최대 실행 시각을 저장
< 두 번째 시나리오 >
1. 타이머 설정(500ms)
2. worker->current_func(work); 함수 실행
3. 'worker->current_func(work)' 코드의 실행 시각이 500ms 이상이 걸리면
타이머 핸들러 함수가 호출됨
4. work_handler_elapsed_timer_func() 타이머 핸들러에서 에러 정보 출력
: 관련 자료 구조
: 프로세스의 콜스택
임베디드 리눅스 개발자분들은 이 포스팅에 올린 자료를 참고해서 더 빨리 퇴근하셨으면 좋겠습니다.
'[Debugging] Tips' 카테고리의 다른 글
[TRACE32] 유저 공간 콜스택 복원하기 - ARMv8(Aarch64) (0) | 2023.05.04 |
---|---|
[리눅스커널] 인터럽트 핸들러 실행 시각 측정 디버깅 패치 (0) | 2023.05.04 |
[리눅스커널] 크래시 유틸리티: files -p '아이노드 주소' + files -d '덴트리 주소' 디버깅 (0) | 2023.05.04 |
[TRACE32] gcore 크래시 유틸리티로 유저 프로세스의 콜스택 보기 (0) | 2023.05.04 |
[리눅스커널][디버깅] ftrace: ftrace를 비활성화하기 (0) | 2023.05.04 |