You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by xi...@apache.org on 2023/06/22 08:08:12 UTC

[nuttx] 02/02: sched:Automatically find deadlocks when assert

This is an automated email from the ASF dual-hosted git repository.

xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit 4ae17a6f7b0961d835a098677a7b7b15ad3aa42d
Author: anjiahao <an...@xiaomi.com>
AuthorDate: Mon Jun 19 10:40:09 2023 +0800

    sched:Automatically find deadlocks when assert
    
    When asserting, automatically analyze whether
    there is a deadlock in the thread, and if there
    is a deadlock, print out the deadlocked thread.
    
    The principle is to analyze whether there is
    a lock ring through the tcb holder.
    
    Signed-off-by: anjiahao <an...@xiaomi.com>
---
 arch/Kconfig          |   6 +++
 include/nuttx/sched.h |  17 ++++++++
 sched/misc/Make.defs  |   4 ++
 sched/misc/assert.c   |  33 +++++++++++++++
 sched/misc/deadlock.c | 115 ++++++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 175 insertions(+)

diff --git a/arch/Kconfig b/arch/Kconfig
index f5a6b1ecb5..c5ca90d438 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -902,6 +902,12 @@ config ARCH_USBDUMP
 	---help---
 		Enable to do USB trace after assertions
 
+config ARCH_DEADLOCKDUMP
+	bool "Dump dead lock thread"
+	default "n"
+	---help---
+		This option will dump the dead lock thread when assert happen..
+
 config ENDIAN_BIG
 	bool "Big Endian Architecture"
 	default n
diff --git a/include/nuttx/sched.h b/include/nuttx/sched.h
index 200a83528e..b77c19106b 100644
--- a/include/nuttx/sched.h
+++ b/include/nuttx/sched.h
@@ -1555,6 +1555,23 @@ pid_t nxsched_getpid(void);
 
 pid_t nxsched_getppid(void);
 
+/****************************************************************************
+ * Name: nxsched_collect_deadlock
+ *
+ * Description:
+ *   Check if there is a deadlock and get the thread pid of the deadlock.
+ *
+ * Input parameters:
+ *   pid   - The array to store the thread pid of the deadlock.
+ *   count - The size of the pid array.
+ *
+ * Returned Value:
+ *   The number of thread deadlocks.
+ *
+ ****************************************************************************/
+
+size_t nxsched_collect_deadlock(FAR pid_t *pid, size_t count);
+
 #undef EXTERN
 #if defined(__cplusplus)
 }
diff --git a/sched/misc/Make.defs b/sched/misc/Make.defs
index 0e955f3ec1..91f156655a 100644
--- a/sched/misc/Make.defs
+++ b/sched/misc/Make.defs
@@ -20,6 +20,10 @@
 
 CSRCS += assert.c panic_notifier.c reboot_notifier.c
 
+ifeq ($(CONFIG_ARCH_DEADLOCKDUMP),y)
+CSRCS += deadlock.c
+endif
+
 # Include init build support
 
 DEPPATH += --dep-path misc
diff --git a/sched/misc/assert.c b/sched/misc/assert.c
index a1d09353be..b232c794cf 100644
--- a/sched/misc/assert.c
+++ b/sched/misc/assert.c
@@ -50,6 +50,8 @@
  * Pre-processor Definitions
  ****************************************************************************/
 
+#define DEADLOCK_MAX 8
+
 #ifndef CONFIG_BOARD_RESET_ON_ASSERT
 #  define CONFIG_BOARD_RESET_ON_ASSERT 0
 #endif
@@ -511,6 +513,31 @@ static void dump_core(pid_t pid)
 }
 #endif
 
+/****************************************************************************
+ * Name: dump_deadlock
+ ****************************************************************************/
+
+#ifdef CONFIG_ARCH_DEADLOCKDUMP
+static void dump_deadlock(void)
+{
+  pid_t deadlock[DEADLOCK_MAX];
+  size_t i = nxsched_collect_deadlock(deadlock, DEADLOCK_MAX);
+
+  if (i > 0)
+    {
+      _alert("Deadlock detected\n");
+      while (i-- > 0)
+        {
+#ifdef CONFIG_SCHED_BACKTRACE
+          sched_dumpstack(deadlock[i]);
+#else
+          _alert("deadlock pid: %d\n", deadlock[i])
+#endif
+        }
+    }
+}
+#endif
+
 /****************************************************************************
  * Public Functions
  ****************************************************************************/
@@ -594,6 +621,12 @@ void _assert(FAR const char *filename, int linenum,
     {
       show_tasks();
 
+#ifdef CONFIG_ARCH_DEADLOCKDUMP
+      /* Deadlock Dump */
+
+      dump_deadlock();
+#endif
+
 #ifdef CONFIG_ARCH_USBDUMP
       /* Dump USB trace data */
 
diff --git a/sched/misc/deadlock.c b/sched/misc/deadlock.c
new file mode 100644
index 0000000000..dad646c273
--- /dev/null
+++ b/sched/misc/deadlock.c
@@ -0,0 +1,115 @@
+/****************************************************************************
+ * sched/misc/deadlock.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/mutex.h>
+#include <nuttx/sched.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+struct deadlock_info_s
+{
+  FAR pid_t *pid;
+  size_t count;
+  size_t found;
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name:find_circular_lock
+ ****************************************************************************/
+
+static void collect_deadlock(FAR struct tcb_s *tcb, FAR void *arg)
+{
+  FAR struct deadlock_info_s *info = arg;
+  size_t index;
+
+  for (index = 0; index < info->found; index++)
+    {
+      if (info->pid[index] == tcb->pid)
+        {
+          return;
+        }
+    }
+
+  for (index = info->found; index < info->count; index++)
+    {
+      FAR sem_t *sem = tcb->waitobj;
+      pid_t next;
+      size_t i;
+
+      if (tcb->task_state != TSTATE_WAIT_SEM || sem == NULL ||
+          !(sem->flags & SEM_TYPE_MUTEX))
+        {
+          return;
+        }
+
+      next = ((FAR mutex_t *)sem)->holder;
+      for (i = info->found; i < index; i++)
+        {
+          if (info->pid[i] == next)
+            {
+              info->found = index;
+              return;
+            }
+        }
+
+      info->pid[index] = tcb->pid;
+      tcb = nxsched_get_tcb(next);
+    }
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nxsched_collect_deadlock
+ *
+ * Description:
+ *   Check if there is a deadlock and get the thread pid of the deadlock.
+ *
+ * Input parameters:
+ *   pid   - The array to store the thread pid of the deadlock.
+ *   count - The size of the pid array.
+ *
+ * Returned Value:
+ *   The number of thread deadlocks.
+ *
+ ****************************************************************************/
+
+size_t nxsched_collect_deadlock(FAR pid_t *pid, size_t count)
+{
+  struct deadlock_info_s info;
+
+  info.pid = pid;
+  info.count = count;
+  info.found = 0;
+  nxsched_foreach(collect_deadlock, &info);
+  return info.found;
+}