thread_analyzer.c 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. /*
  2. * Copyright (c) 2019 - 2020 Nordic Semiconductor ASA
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. /** @file
  7. * @brief Thread analyzer implementation
  8. */
  9. #include <kernel.h>
  10. #include <debug/thread_analyzer.h>
  11. #include <debug/stack.h>
  12. #include <kernel.h>
  13. #include <logging/log.h>
  14. #include <stdio.h>
  15. LOG_MODULE_REGISTER(thread_analyzer, CONFIG_THREAD_ANALYZER_LOG_LEVEL);
  16. #if IS_ENABLED(CONFIG_THREAD_ANALYZER_USE_PRINTK)
  17. #define THREAD_ANALYZER_PRINT(...) printk(__VA_ARGS__)
  18. #define THREAD_ANALYZER_FMT(str) str "\n"
  19. #define THREAD_ANALYZER_VSTR(str) (str)
  20. #else
  21. #define THREAD_ANALYZER_PRINT(...) LOG_INF(__VA_ARGS__)
  22. #define THREAD_ANALYZER_FMT(str) str
  23. #define THREAD_ANALYZER_VSTR(str) log_strdup(str)
  24. #endif
  25. /* @brief Maximum length of the pointer when converted to string
  26. *
  27. * Pointer is converted to string in hexadecimal form.
  28. * It would use 2 hex digits for every single byte of the pointer
  29. * but some implementations adds 0x prefix when used with %p format option.
  30. */
  31. #define PTR_STR_MAXLEN (sizeof(void *) * 2 + 2)
  32. static void thread_print_cb(struct thread_analyzer_info *info)
  33. {
  34. size_t pcnt = (info->stack_used * 100U) / info->stack_size;
  35. #ifdef CONFIG_THREAD_RUNTIME_STATS
  36. THREAD_ANALYZER_PRINT(
  37. THREAD_ANALYZER_FMT(
  38. " %-20s: STACK: unused %zu usage %zu / %zu (%zu %%); CPU: %u %%"),
  39. THREAD_ANALYZER_VSTR(info->name),
  40. info->stack_size - info->stack_used, info->stack_used,
  41. info->stack_size, pcnt,
  42. info->utilization);
  43. #else
  44. THREAD_ANALYZER_PRINT(
  45. THREAD_ANALYZER_FMT(
  46. " %-20s: unused %zu usage %zu / %zu (%zu %%)"),
  47. THREAD_ANALYZER_VSTR(info->name),
  48. info->stack_size - info->stack_used, info->stack_used,
  49. info->stack_size, pcnt);
  50. #endif
  51. }
  52. static void thread_analyze_cb(const struct k_thread *cthread, void *user_data)
  53. {
  54. struct k_thread *thread = (struct k_thread *)cthread;
  55. #ifdef CONFIG_THREAD_RUNTIME_STATS
  56. k_thread_runtime_stats_t rt_stats_all;
  57. k_thread_runtime_stats_t rt_stats_thread;
  58. int ret;
  59. #endif
  60. size_t size = thread->stack_info.size;
  61. thread_analyzer_cb cb = user_data;
  62. struct thread_analyzer_info info;
  63. char hexname[PTR_STR_MAXLEN + 1];
  64. const char *name;
  65. size_t unused;
  66. int err;
  67. name = k_thread_name_get((k_tid_t)thread);
  68. if (!name || name[0] == '\0') {
  69. name = hexname;
  70. snprintk(hexname, sizeof(hexname), "%p", (void *)thread);
  71. }
  72. err = k_thread_stack_space_get(thread, &unused);
  73. if (err) {
  74. THREAD_ANALYZER_PRINT(
  75. THREAD_ANALYZER_FMT(
  76. " %-20s: unable to get stack space (%d)"),
  77. name, err);
  78. unused = 0;
  79. }
  80. info.name = name;
  81. info.stack_size = size;
  82. info.stack_used = size - unused;
  83. #ifdef CONFIG_THREAD_RUNTIME_STATS
  84. ret = 0;
  85. if (k_thread_runtime_stats_get(thread, &rt_stats_thread) != 0) {
  86. ret++;
  87. }
  88. if (k_thread_runtime_stats_all_get(&rt_stats_all) != 0) {
  89. ret++;
  90. }
  91. if (ret == 0) {
  92. info.utilization = (rt_stats_thread.execution_cycles * 100U) /
  93. rt_stats_all.execution_cycles;
  94. }
  95. #endif
  96. cb(&info);
  97. }
  98. void thread_analyzer_run(thread_analyzer_cb cb)
  99. {
  100. if (IS_ENABLED(CONFIG_THREAD_ANALYZER_RUN_UNLOCKED)) {
  101. k_thread_foreach_unlocked(thread_analyze_cb, cb);
  102. } else {
  103. k_thread_foreach(thread_analyze_cb, cb);
  104. }
  105. }
  106. void thread_analyzer_print(void)
  107. {
  108. THREAD_ANALYZER_PRINT(THREAD_ANALYZER_FMT("Thread analyze:"));
  109. thread_analyzer_run(thread_print_cb);
  110. }
  111. #if IS_ENABLED(CONFIG_THREAD_ANALYZER_AUTO)
  112. void thread_analyzer_auto(void)
  113. {
  114. for (;;) {
  115. thread_analyzer_print();
  116. k_sleep(K_SECONDS(CONFIG_THREAD_ANALYZER_AUTO_INTERVAL));
  117. }
  118. }
  119. K_THREAD_DEFINE(thread_analyzer,
  120. CONFIG_THREAD_ANALYZER_AUTO_STACK_SIZE,
  121. thread_analyzer_auto,
  122. NULL, NULL, NULL,
  123. K_LOWEST_APPLICATION_THREAD_PRIO,
  124. 0, 0);
  125. #endif