You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by cc...@apache.org on 2018/11/13 17:18:30 UTC

[mynewt-core] branch master updated (2a39ae4 -> ef1f060)

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

ccollins pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-core.git.


    from 2a39ae4  Merge pull request #1498 from kasjer/fcb2
     new 06cefe8  sys/sys: DEBUG_ASSERT()
     new ef1f060  sys/fault: Global fault management

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 {hw/charge-control => sys/fault/fault_cli}/pkg.yml |  18 +-
 sys/fault/fault_cli/src/fault_cli.c                | 235 ++++++++++++++
 .../blinker => sys/fault/fault_cli}/syscfg.yml     |   8 +-
 sys/fault/include/fault/fault.h                    | 278 +++++++++++++++++
 sys/{id => fault}/pkg.yml                          |  23 +-
 sys/fault/src/fault.c                              | 339 +++++++++++++++++++++
 sys/fault/src/fault_conf.c                         | 111 +++++++
 .../src/tbb.h => sys/fault/src/fault_priv.h        |  25 +-
 {hw/battery => sys/fault}/syscfg.yml               |  26 +-
 sys/sys/include/sys/debug_panic.h                  |   6 +
 10 files changed, 1013 insertions(+), 56 deletions(-)
 copy {hw/charge-control => sys/fault/fault_cli}/pkg.yml (79%)
 create mode 100644 sys/fault/fault_cli/src/fault_cli.c
 copy {hw/util/blinker => sys/fault/fault_cli}/syscfg.yml (87%)
 create mode 100644 sys/fault/include/fault/fault.h
 copy sys/{id => fault}/pkg.yml (75%)
 create mode 100644 sys/fault/src/fault.c
 create mode 100644 sys/fault/src/fault_conf.c
 copy apps/testbench/src/tbb.h => sys/fault/src/fault_priv.h (80%)
 copy {hw/battery => sys/fault}/syscfg.yml (67%)


[mynewt-core] 02/02: sys/fault: Global fault management

Posted by cc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ccollins pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-core.git

commit ef1f06024dca9a6d2aabe4a4bb64b57b5183432a
Author: Christopher Collins <cc...@apache.org>
AuthorDate: Mon Nov 5 13:57:56 2018 -0800

    sys/fault: Global fault management
    
    This package is intended to handle unexpected runtime failures (e.g.,
    hardware failures, heap allocation failures, etc).  An application might
    handle such failures with any of the following actions:
    
        * Log the failure
        * Retry the failed operation
        * Attempt to recover with a system reboot
        * Disable the failing module entirely
    
    Which action(s) the application takes might depend on how much, and how
    chronically, the operation has failed.
    
    For a more complete description of this package, please see the comments
    at the top of `sys/fault/include/fault.h`.
---
 sys/fault/fault_cli/pkg.yml         |  32 ++++
 sys/fault/fault_cli/src/fault_cli.c | 235 +++++++++++++++++++++++++
 sys/fault/fault_cli/syscfg.yml      |  24 +++
 sys/fault/include/fault/fault.h     | 278 +++++++++++++++++++++++++++++
 sys/fault/pkg.yml                   |  36 ++++
 sys/fault/src/fault.c               | 339 ++++++++++++++++++++++++++++++++++++
 sys/fault/src/fault_conf.c          | 111 ++++++++++++
 sys/fault/src/fault_priv.h          |  31 ++++
 sys/fault/syscfg.yml                |  39 +++++
 9 files changed, 1125 insertions(+)

diff --git a/sys/fault/fault_cli/pkg.yml b/sys/fault/fault_cli/pkg.yml
new file mode 100644
index 0000000..9b3a8a0
--- /dev/null
+++ b/sys/fault/fault_cli/pkg.yml
@@ -0,0 +1,32 @@
+#
+# 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.
+#
+
+pkg.name: sys/fault/fault_cli
+pkg.description: CLI for the sys/fault package.
+pkg.keywords:
+    - fault
+
+pkg.deps:
+    - "@apache-mynewt-core/sys/console/full"
+    - "@apache-mynewt-core/sys/fault"
+    - "@apache-mynewt-core/sys/shell"
+    - "@apache-mynewt-core/util/parse"
+
+pkg.init:
+    fault_cli_init: 'MYNEWT_VAL(FAULT_CLI_SYSINIT_STAGE)'
diff --git a/sys/fault/fault_cli/src/fault_cli.c b/sys/fault/fault_cli/src/fault_cli.c
new file mode 100644
index 0000000..d2339b8
--- /dev/null
+++ b/sys/fault/fault_cli/src/fault_cli.c
@@ -0,0 +1,235 @@
+/*
+ * 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.
+ */
+
+#include <inttypes.h>
+#include <stddef.h>
+#include <string.h>
+#include <limits.h>
+#include "os/mynewt.h"
+#include "console/console.h"
+#include "parse/parse.h"
+#include "shell/shell.h"
+#include "fault/fault.h"
+
+static int fault_cli_cmd_fn(int argc, char **argv);
+
+static const struct shell_cmd_help fault_cli_help = {
+    .summary = "Fault management",
+    .usage = 
+        "\n"
+        "fault chronls\n"
+        "    Lists the chronic fault failure counts.\n"
+        "fault chronset <fault-domain> <count>\n"
+        "    Sets and persists the specified chronic fault failure count for\n"
+        "    the given domain.\n"
+        "fault chronclr\n"
+        "    Sets all chronic fault failure counts to 0 and persists the\n"
+        "    counts.\n"
+        "fault fatalfail <fault-domain> [uint-param]\n"
+        "    Simulates a fatal error for the given domain.\n"
+        "fault fatalgood <fault-domain> [uint-param]\n"
+        "    Simulates a fatal success for the given domain.\n",
+};
+
+static const struct shell_cmd fault_cli_cmd = {
+    .sc_cmd = "fault",
+    .sc_cmd_func = fault_cli_cmd_fn,
+    .help = &fault_cli_help,
+};
+
+static int
+fault_cli_parse_domain(const char *arg)
+{
+    int dom;
+    const char *name;
+    int rc;
+
+    /* First, try to parse a numeric ID. */
+    dom = parse_ull_bounds(arg, 0, MYNEWT_VAL(FAULT_MAX_DOMAINS), &rc);
+    if (rc == 0) {
+        return dom;
+    }
+
+    /* Otherwise, see if the user typed the name of a domain. */
+    for (dom = 0; dom < MYNEWT_VAL(FAULT_MAX_DOMAINS); dom++) {
+        name = fault_domain_name(dom);
+        if (name != NULL && strcasecmp(arg, name) == 0) {
+            return dom;
+        }
+    }
+
+    return -1;
+}
+
+static int
+fault_cli_chronls(int argc, char **argv)
+{
+    const char *name;
+    uint8_t count;
+    int rc;
+    int i;
+
+    for (i = 0; i < MYNEWT_VAL(FAULT_MAX_DOMAINS); i++) {
+        rc = fault_get_chronic_count(i, &count);
+        assert(rc == 0);
+
+        name = fault_domain_name(i);
+        if (name == NULL) {
+            name = "";
+        }
+        console_printf("(%d) %s: %d\n", i, name, count);
+    }
+
+    return 0;
+}
+
+static int
+fault_cli_chronset(int argc, char **argv)
+{
+    uint8_t count;
+    int dom;
+    int rc;
+
+    if (argc < 2) {
+        return SYS_EINVAL;
+    }
+
+    dom = fault_cli_parse_domain(argv[0]);
+    if (dom == -1) {
+        console_printf("invalid domain string\n");
+        return SYS_EINVAL;
+    }
+
+    count = parse_ull_bounds(argv[1], 0, UINT8_MAX, &rc);
+    if (rc != 0) {
+        console_printf("invalid failure count\n");
+        return rc;
+    }
+
+    rc = fault_set_chronic_count(dom, count);
+    if (rc != 0) {
+        return rc;
+    }
+
+    return 0;
+}
+
+static int
+fault_cli_chronclr(int argc, char **argv)
+{
+    int dom;
+    int rc;
+
+    for (dom = 0; dom < MYNEWT_VAL(FAULT_MAX_DOMAINS); dom++) {
+        rc = fault_set_chronic_count(dom, 0);
+        if (rc != 0) {
+            return rc;
+        }
+    }
+
+    return 0;
+}
+
+static int
+fault_cli_fatal_gen(int argc, char **argv, bool is_failure)
+{
+    uintptr_t int_arg;
+    int dom;
+    int rc;
+
+    if (argc < 1) {
+        return SYS_EINVAL;
+    }
+
+    dom = fault_cli_parse_domain(argv[0]);
+    if (dom == -1) {
+        console_printf("invalid domain string\n");
+        return SYS_EINVAL;
+    }
+
+    if (argc >= 2) {
+        int_arg = parse_ull_bounds(argv[1], 0, UINT_MAX, &rc);
+        if (rc != 0)
+        {
+            console_printf("invalid fault argument (`arg`)\n");
+            return rc;
+        }
+    } else {
+        int_arg = 0;
+    }
+
+    fault_fatal(dom, (void *)int_arg, is_failure);
+
+    return 0;
+}
+
+static int
+fault_cli_fatalfail(int argc, char **argv)
+{
+    return fault_cli_fatal_gen(argc, argv, true);
+}
+
+static int
+fault_cli_fatalgood(int argc, char **argv)
+{
+    return fault_cli_fatal_gen(argc, argv, false);
+}
+
+static int
+fault_cli_cmd_fn(int argc, char **argv)
+{
+    /* Shift initial "fault" argument. */
+    argc--;
+    argv++;
+
+    if (argc < 1) {
+        return SYS_EINVAL;
+    }
+
+    if (strcmp(argv[0], "chronls") == 0) {
+        return fault_cli_chronls(argc - 1, argv + 1);
+    }
+
+    if (strcmp(argv[0], "chronset") == 0) {
+        return fault_cli_chronset(argc - 1, argv + 1);
+    }
+
+    if (strcmp(argv[0], "chronclr") == 0) {
+        return fault_cli_chronclr(argc - 1, argv + 1);
+    }
+
+    if (strcmp(argv[0], "fatalfail") == 0) {
+        return fault_cli_fatalfail(argc - 1, argv + 1);
+    }
+
+    if (strcmp(argv[0], "fatalgood") == 0) {
+        return fault_cli_fatalgood(argc - 1, argv + 1);
+    }
+
+    return SYS_EINVAL;
+}
+
+void
+fault_cli_init(void)
+{
+    int rc;
+
+    rc = shell_cmd_register(&fault_cli_cmd);
+    SYSINIT_PANIC_ASSERT(rc == 0);
+}
diff --git a/sys/fault/fault_cli/syscfg.yml b/sys/fault/fault_cli/syscfg.yml
new file mode 100644
index 0000000..4d0540f
--- /dev/null
+++ b/sys/fault/fault_cli/syscfg.yml
@@ -0,0 +1,24 @@
+#
+# 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.
+#
+
+syscfg.defs:
+    FAULT_CLI_SYSINIT_STAGE:
+        description: >
+            Sysinit stage for the fault CLI.
+        value: 520
diff --git a/sys/fault/include/fault/fault.h b/sys/fault/include/fault/fault.h
new file mode 100644
index 0000000..0ac146b
--- /dev/null
+++ b/sys/fault/include/fault/fault.h
@@ -0,0 +1,278 @@
+/*
+ * 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.
+ */
+
+#ifndef H_FAULT_
+#define H_FAULT_
+
+/**
+ * FAULT
+ * This package tracks, logs, and recovers from errors.  
+ *
+ * TERMS
+ * Domain:
+ *     Identifies the part of the system that failed (e.g., BLE, file system,
+ *     etc.).
+ *
+ * Recorder:
+ *     Tracks successes and failures of a particular operation (e.g., BLE
+ *     advertisement attempts).  Each recorder has an associated acute failure
+ *     count.
+ *
+ * Acute failure count:
+ *     The number of times a particular operation has failed.  Each failure
+ *     increments the count; each success decrements it.
+ *
+ * Chronic failure count:
+ *     Indicates the long-term stability of a particular domain.  The set of
+ *     chronic failure counts (one per domain) is persisted to flash.
+ *
+ * Warn threshold:
+ *     When a recorder's acute failure count count increases to its warn
+ *     threshold, the recorder enters the warn state.  The application is
+ *     notified of the state change.
+ *
+ * Error threshold:
+ *     When a recorder's fault count increases to its error threshold, the
+ *     recorder enters the error state.  When this happens, the domain's
+ *     chronic failure count is increased and persisted.  In addition, the
+ *     application is notified of the state change.  The application should
+ *     perform either of the following actions when the error threshold is
+ *     reached:
+ *         o Reboot the system, or
+ *         o Disable the domain entirely.
+ *
+ *     It may make sense to choose which action to take depending on the
+ *     domain's chronic fail count.
+ *
+ * Fatal fault:
+ *     A fatal fault is any operation with an error threshold of 1.  That is, a
+ *     fatal failure always triggers a transition to the error state.
+ */
+
+#include "debounce/debounce.h"
+
+#define FAULT_STATE_GOOD        0
+#define FAULT_STATE_WARN        1
+#define FAULT_STATE_ERROR       2
+
+/**
+ * Fault recorder: Tracks the occurrence of a specific fault type.
+ *
+ * All members should be considered private.
+ */
+struct fault_recorder {
+    /* Config. */
+    int domain_id;
+    void *arg;
+
+    /* State. */
+    struct debouncer deb;
+};
+
+typedef void fault_thresh_fn(int domain_id, int prev_state, int state,
+                             void *arg);
+
+/**
+ * @brief Configures a global callback to be executed when a fault state change
+ * occurs.
+ *
+ * @param cb                    The callback to confiure.
+ */
+void fault_configure_cb(fault_thresh_fn *cb);
+
+/**
+ * @brief processes the result of a fault-capable operation.
+ *
+ * See `fault_success()` and `fault_failure()` for details.  All else being
+ * equal, `fault_success()` and `fault_failure()` should be preferred over this
+ * function for reasons of clarity.
+ *
+ * @param recorder              The fault recorder associated with the
+ *                                  operation.
+ * @param is_failure            Whether the operation failed.
+ */
+int fault_process(struct fault_recorder *recorder, bool is_failure);
+
+/**
+ * @brief Records a successful operation for the provided fault recorder.
+ *
+ * Each successful operation decrements the acute failure count of the provided
+ * fault recorder, or has no effect if the failure count is already 0.  If this
+ * function causes the acute failure count to reach 0, the chronic failure
+ * count of the relevant faults is decreased and persisted to flash (if it is
+ * currently greated than 0).
+ *
+ * @param recorder              The fault recorder to register a success with.
+ *
+ * @return                      The recorder's current state.
+ */
+int fault_success(struct fault_recorder *recorder);
+
+/**
+ * @brief Records a failed operation for the provided fault recorder.
+ *
+ * Each failed operation increments the failure count of the provided fault
+ * recorder.  If this function causes the failure count to reach the provided
+ * recorder's warn threshold or error threshold, the application is notified
+ * via the global "fault thresh" callback.
+ *
+ * @param recorder              The fault recorder to register a failure with.
+ *
+ * @return                      The recorder's current state.
+ */
+int fault_failure(struct fault_recorder *recorder);
+
+/**
+ * @brief processes the result of a fatal operation.
+ *
+ * See `fault_fatal_success()` and `fault_fatal_failure()` for details.  All
+ * else being equal, `fault_fatal_success()` and `fault_fatal_failure()` should
+ * be preferred over this function for reasons of clarity.
+ *
+ * @param fault_domain          The domain associated with the operation.
+ * @param arg                   Fault-specific argument to pass to the global
+ *                                  calback.
+ * @param is_failure            Whether the operation failed.
+ */
+void fault_fatal(int domain_id, void *arg, bool is_failure);
+
+/**
+ * @brief Records a success for a fatal operation.
+ *
+ * This function decreases the specified fault type's chronic failure count,
+ * or has no effect if the count is already 0.
+ *
+ * @param domain_id             The domain associated with the successful
+ *                                  operation.
+ * @param arg                   Fault-specific argument to pass to the global
+ *                                  calback.
+ */
+void fault_fatal_success(int domain_id, void *arg);
+
+/**
+ * @brief Records a failure for a fatal operation.
+ *
+ * This function triggers an error for the specified domain.  That is, it
+ * increases the specified domain's chronic failure count, and notifies the
+ * application via the "global thresh" callback.
+ *
+ * @param fault_domain          The fault type associated with the failed
+ *                                  operation.
+ * @param arg                   Fault-specific argument to pass to the global
+ *                                  calback.
+ */
+void fault_fatal_failure(int domain_id, void *arg);
+
+/**
+ * @brief Retrieves the chronic failure associated with the specified domian.
+ *
+ * @param domain_id             The domain to query.
+ * @param out_count             On success, the requested count gets written
+ *                                  here.
+ *
+ * @return                      0 on success;
+ *                              SYS_EINVAL if the specified ID does not
+ *                                  correspond to a registered domain.
+ */
+int fault_get_chronic_count(int domain_id, uint8_t *out_count);
+
+/**
+ * @brief Sets the chronic failure associated with the specified domian.
+ *
+ * @param domain_id             The domain whose chronic failure count should
+ *                                  be set.
+ * @param count                 The chronic failure count to set.
+ *
+ *                              SYS_EINVAL if the specified ID does not
+ *                                  correspond to a registered domain.
+ */
+int fault_set_chronic_count(int domain_id, uint8_t count);
+
+/**
+ * @brief Retrieves the name of the specified fault domain.
+ *
+ * Note: if the FAULT_DOMAIN_NAMES setting is disabled, this function always
+ * returns NULL.
+ *
+ * @param domain                The domain to query.
+ *
+ * @return                      The name of the specified domain, or NULL if
+ *                                  the specified ID does not correspond to a
+ *                                  registered domain.
+ */
+const char *fault_domain_name(int domain_id);
+
+/**
+ * @brief Constructs a new fault recorder for tracking errors.
+ *
+ * @param recorder              The fault recorder to initialize.
+ * @param fault_domain          The domain of faults to track.
+ * @param warn_thresh           The application is notified when the recorder's
+ *                                  acute failure count reaches this value.
+ * @param error_thresh          The domain's chronic failure
+ *                                  count is increased, and the application is
+ *                                  notified, when the recorder's acute failure
+ *                                  count reaches this
+ *                                  value.
+ * @param arg                   Fault-specific argument.
+ *
+ * @return                      0 on success; SYS_E[...] on error.
+ */
+int fault_recorder_init(struct fault_recorder *recorder,
+                        int domain_id,
+                        uint16_t warn_thresh,
+                        uint16_t error_thresh,
+                        void *arg);
+
+
+/**
+ * @brief Private function; use `fault_register_domain` instead.
+ */
+int fault_register_domain_priv(int domain_id, uint8_t success_delta,
+                               uint8_t fail_delta, const char *name);
+
+/**
+ * @brief Registers a fault domain.
+ *
+ * @param domain_id             The unique ID of the domain to register.
+ * @param success_delta         The amount the domain's chronic failure count
+ *                                  decreases when a recorder's acute failure
+ *                                  count reaches 0.
+ * @param failure_delta         The amount the domain's chronic failure count
+ *                                  increases when a recorder's acute failure
+ *                                  count reaches the error threshold.
+ * @param name                  The name of the domain; ignored if
+ *                                  FAULT_DOMAI_NAMES is disabled.
+ *
+ * @return                      0 on success; SYS_E[...] on error.
+ */ 
+static inline int
+fault_register_domain(int domain_id, uint8_t success_delta,
+                      uint8_t failure_delta, const char *name)
+{
+#if !MYNEWT_VAL(FAULT_DOMAIN_NAMES)
+    /* Allow hardcoded strings to be optimized out. */
+    name = NULL;
+#endif
+
+    return fault_register_domain_priv(domain_id, success_delta, failure_delta,
+                                      name);
+}
+
+#endif
diff --git a/sys/fault/pkg.yml b/sys/fault/pkg.yml
new file mode 100644
index 0000000..64954f6
--- /dev/null
+++ b/sys/fault/pkg.yml
@@ -0,0 +1,36 @@
+#
+# 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.
+#
+
+pkg.name: sys/fault
+pkg.type: lib
+pkg.description: Tracks, logs, and recovers from errors.  
+pkg.keywords:
+    - fault
+
+pkg.deps:
+    - "@apache-mynewt-core/encoding/base64"
+    - "@apache-mynewt-core/kernel/os"
+    - "@apache-mynewt-core/sys/config"
+    - "@apache-mynewt-core/util/debounce"
+
+pkg.deps.FAULT_CLI:
+    - "@apache-mynewt-core/sys/fault/fault_cli"
+
+pkg.init:
+    fault_init: MYNEWT_VAL(FAULT_SYSINIT_STAGE)
diff --git a/sys/fault/src/fault.c b/sys/fault/src/fault.c
new file mode 100644
index 0000000..22f28bb
--- /dev/null
+++ b/sys/fault/src/fault.c
@@ -0,0 +1,339 @@
+/*
+ * 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.
+ */
+
+#include "os/mynewt.h"
+
+#include "modlog/modlog.h"
+#include "data_recorder/faults.h"
+#include "fault/fault.h"
+#include "fault_priv.h"
+
+struct fault_domain {
+    uint8_t success_delta;
+    uint8_t failure_delta;
+
+#if MYNEWT_VAL(FAULT_DOMAIN_NAMES)
+    const char *name;
+#endif
+};
+
+uint8_t fault_chronic_counts[MYNEWT_VAL(FAULT_MAX_DOMAINS)];
+static struct fault_domain fault_domains[MYNEWT_VAL(FAULT_MAX_DOMAINS)];
+
+static fault_thresh_fn *fault_thresh_cb;
+
+void
+fault_configure_cb(fault_thresh_fn *cb)
+{
+    fault_thresh_cb = cb;
+}
+
+static bool
+fault_domain_is_registered(const struct fault_domain *dom)
+{
+    return dom->failure_delta != 0;
+}
+
+static struct fault_domain *
+fault_get_domain(int domain_id)
+{
+    if (domain_id < 0 || domain_id >= MYNEWT_VAL(FAULT_MAX_DOMAINS)) {
+        return NULL;
+    }
+
+    return &fault_domains[domain_id];
+}
+
+static struct fault_domain *
+fault_get_registered_domain(int domain_id)
+{
+    struct fault_domain *dom;
+
+    dom = fault_get_domain(domain_id);
+    if (dom == NULL) {
+        return NULL;
+    }
+
+    if (!fault_domain_is_registered(dom)) {
+        return NULL;
+    }
+
+    return dom;
+}
+
+static bool
+fault_recorder_is_saturated(const struct fault_recorder *recorder)
+{
+    return debouncer_val(&recorder->deb) == recorder->deb.max;
+}
+
+static int
+fault_recorder_state(const struct fault_recorder *recorder)
+{
+    if (fault_recorder_is_saturated(recorder)) {
+        return FAULT_STATE_ERROR;
+    } else if (debouncer_state(&recorder->deb)) {
+        return FAULT_STATE_WARN;
+    } else {
+        return FAULT_STATE_GOOD;
+    }
+}
+
+static int
+fault_adjust_chronic_count(int domain_id, int delta)
+{
+    int count;
+
+    /* Increase and persist the chronic failure count if it would not wrap. */
+    count = fault_chronic_counts[domain_id] + delta;
+    if (count < 0) {
+        count = 0;
+    } else if (count > UINT8_MAX) {
+        count = UINT8_MAX;
+    }
+
+    return fault_set_chronic_count(domain_id, count);
+}
+
+static int
+fault_decrease_chronic_count(int domain_id)
+{
+    const struct fault_domain *dom;
+
+    dom = fault_get_registered_domain(domain_id);
+    assert(dom != NULL);
+
+    return fault_adjust_chronic_count(domain_id, -dom->success_delta);
+}
+
+static int
+fault_increase_chronic_count(int domain_id)
+{
+    const struct fault_domain *dom;
+
+    dom = fault_get_registered_domain(domain_id);
+    assert(dom != NULL);
+
+    return fault_adjust_chronic_count(domain_id, dom->failure_delta);
+}
+
+/**
+ * @brief processes the result of a fault-capable operation.
+ *
+ * @param recorder              The fault recorder associated with the
+ *                                  operation.
+ * @param success               Whether the operation was successful.
+ */
+int
+fault_process(struct fault_recorder *recorder, bool is_failure)
+{
+    int prev_state;
+    int state;
+    int delta;
+
+    prev_state = fault_recorder_state(recorder);
+
+    /* There is nothing to do if we are already at the maximum fault count. */
+    if (is_failure && prev_state == FAULT_STATE_ERROR) {
+        return FAULT_STATE_ERROR;
+    }
+
+    if (is_failure) {
+        delta = 1;
+    } else {
+        delta = -1;
+    }
+
+    debouncer_adjust(&recorder->deb, delta);
+    state = fault_recorder_state(recorder);
+
+    if (state == FAULT_STATE_GOOD) {
+        /* The domain seems to be working; decrease chronic failure count. */
+        fault_decrease_chronic_count(recorder->domain_id);
+    } else {
+        /* Fault detected; trigger a crash in debug builds. */
+        DEBUG_PANIC();
+
+        if (state == FAULT_STATE_ERROR) {
+            fault_increase_chronic_count(recorder->domain_id);
+        }
+    }
+
+    /* Notify the application if the fault state changed. */
+    if (prev_state != state && fault_thresh_cb != NULL) {
+        fault_thresh_cb(recorder->domain_id, prev_state, state, recorder->arg);
+    }
+
+    return state;
+}
+
+int
+fault_success(struct fault_recorder *recorder)
+{
+    return fault_process(recorder, false);
+}
+
+int
+fault_failure(struct fault_recorder *recorder)
+{
+    return fault_process(recorder, true);
+}
+
+void
+fault_fatal(int domain_id, void *arg, bool is_failure)
+{
+    struct fault_recorder recorder;
+    int rc;
+
+    rc = fault_recorder_init(&recorder, domain_id, 1, 1, arg);
+    assert(rc == 0);
+
+    fault_process(&recorder, is_failure);
+}
+
+void
+fault_fatal_success(int domain_id, void *arg)
+{
+    fault_fatal(domain_id, arg, false);
+}
+
+void
+fault_fatal_failure(int domain_id, void *arg)
+{
+    fault_fatal(domain_id, arg, true);
+}
+
+int
+fault_get_chronic_count(int domain_id, uint8_t *out_count)
+{
+    if (domain_id < 0 || domain_id >= MYNEWT_VAL(FAULT_MAX_DOMAINS)) {
+        return SYS_EINVAL;
+    }
+
+    *out_count = fault_chronic_counts[domain_id];
+    return 0;
+}
+
+int
+fault_set_chronic_count(int domain_id, uint8_t count)
+{
+    int rc;
+
+    if (fault_chronic_counts[domain_id] == count) {
+        return 0;
+    }
+
+    fault_chronic_counts[domain_id] = count;
+
+    rc = fault_conf_persist_chronic_counts();
+    DEBUG_ASSERT(rc == 0);
+
+    return rc;
+}
+
+int
+fault_recorder_init(struct fault_recorder *recorder,
+                    int domain_id,
+                    uint16_t warn_thresh,
+                    uint16_t error_thresh,
+                    void *arg)
+{
+    int rc;
+
+    if (fault_get_registered_domain(domain_id) == NULL) {
+        return SYS_EINVAL;
+    }
+
+    if (warn_thresh > error_thresh) {
+        return SYS_EINVAL;
+    }
+
+    rc = debouncer_init(&recorder->deb, 0, warn_thresh, error_thresh);
+    if (rc != 0) {
+        return rc;
+    }
+
+    /* If this fault recorder is associated with a chronically-failing domain,
+     * start the recorder with an initial acute failure count.  For recorders
+     * with high failure thresholds, this ensures that at least a few successes
+     * are required before the domain is considered stable.
+     */
+    if (fault_chronic_counts[domain_id] > 0) {
+        debouncer_set(&recorder->deb, warn_thresh / 2);
+    }
+
+    recorder->domain_id = domain_id;
+    recorder->arg = arg;
+
+    return 0;
+}
+
+const char *
+fault_domain_name(int domain_id)
+{
+#if MYNEWT_VAL(FAULT_DOMAIN_NAMES)
+    const struct fault_domain *domain;
+
+    domain = fault_get_registered_domain(domain_id);
+    if (domain != NULL) {
+        return domain->name;
+    }
+#endif
+
+    return NULL;
+}
+
+int
+fault_register_domain_priv(int domain_id, uint8_t success_delta,
+                           uint8_t failure_delta, const char *name)
+{
+    struct fault_domain *dom;
+
+    if (failure_delta == 0) {
+        return SYS_EINVAL;
+    }
+
+    dom = fault_get_domain(domain_id);
+    if (dom == NULL) {
+        return SYS_EINVAL;
+    }
+
+    if (fault_domain_is_registered(dom)) {
+        return SYS_EALREADY;
+    }
+
+    *dom = (struct fault_domain) {
+        .success_delta = success_delta,
+        .failure_delta = failure_delta,
+#if MYNEWT_VAL(FAULT_DOMAIN_NAMES)
+        .name = name,
+#endif
+    };
+
+    return 0;
+}
+
+void
+fault_init(void)
+{
+    int rc;
+
+    rc = fault_conf_init();
+    SYSINIT_PANIC_ASSERT(rc == 0);
+}
diff --git a/sys/fault/src/fault_conf.c b/sys/fault/src/fault_conf.c
new file mode 100644
index 0000000..ff785bb
--- /dev/null
+++ b/sys/fault/src/fault_conf.c
@@ -0,0 +1,111 @@
+/*
+ * 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.
+ */
+
+#include <assert.h>
+#include <string.h>
+#include "base64/base64.h"
+#include "config/config.h"
+#include "fault_priv.h"
+
+static char *fault_conf_get(int argc, char **argv, char *buf, int max_len);
+static int fault_conf_set(int argc, char **argv, char *val);
+static int fault_conf_commit(void);
+static int fault_conf_export(void (*func)(char *name, char *val),
+        enum conf_export_tgt tgt);
+
+static struct conf_handler fault_conf_handler = {
+    .ch_name = "fault",
+    .ch_get = fault_conf_get,
+    .ch_set = fault_conf_set,
+    .ch_commit = fault_conf_commit,
+    .ch_export = fault_conf_export
+};
+
+/** Converts in-RAM setting to a config-friendly string. */
+static char *
+fault_conf_get(int argc, char **argv, char *buf, int max_len)
+{
+    if (argc == 1) {
+        if (!strcmp(argv[0], "chronfail")) {
+            return conf_str_from_bytes(fault_chronic_counts,
+                    sizeof fault_chronic_counts,
+                    buf, max_len);
+        }
+    }
+    return NULL;
+}
+
+/** Converts config string to binary in-RAM value. */
+static int
+fault_conf_set(int argc, char **argv, char *val)
+{
+    int decode_len;
+
+    if (argc == 1) {
+        if (strcmp(argv[0], "chronfail") == 0) {
+            decode_len = base64_decode_len(val);
+            if (decode_len > MYNEWT_VAL(FAULT_MAX_DOMAINS)) {
+                DEBUG_PANIC();
+                return SYS_ENOMEM;
+            }
+
+            memset(fault_chronic_counts, 0, sizeof fault_chronic_counts);
+            base64_decode(val, fault_chronic_counts);
+
+            return 0;
+        }
+    }
+    return -1;
+}
+
+static int
+fault_conf_commit(void)
+{
+    return 0;
+}
+
+static int
+fault_conf_export(void (*func)(char *name, char *val),
+        enum conf_export_tgt tgt)
+{
+    char buf[BASE64_ENCODE_SIZE(sizeof fault_chronic_counts) + 1];
+
+    conf_str_from_bytes(fault_chronic_counts, sizeof fault_chronic_counts,
+        buf, sizeof buf);
+
+    func("fault/chronfail", buf);
+    return 0;
+}
+
+int
+fault_conf_persist_chronic_counts(void)
+{
+    char buf[BASE64_ENCODE_SIZE(sizeof fault_chronic_counts) + 1];
+
+    conf_str_from_bytes(fault_chronic_counts, sizeof fault_chronic_counts,
+        buf, sizeof buf);
+
+    return conf_save_one("fault/chronfail", buf);
+}
+
+int
+fault_conf_init(void)
+{
+    return conf_register(&fault_conf_handler);
+}
diff --git a/sys/fault/src/fault_priv.h b/sys/fault/src/fault_priv.h
new file mode 100644
index 0000000..f0aee06
--- /dev/null
+++ b/sys/fault/src/fault_priv.h
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+#ifndef H_FAULT_PRIV_
+#define H_FAULT_PRIV_
+
+#include "os/mynewt.h"
+#include "fault/fault.h"
+
+extern uint8_t fault_chronic_counts[MYNEWT_VAL(FAULT_MAX_DOMAINS)];
+
+int fault_conf_persist_chronic_counts(void);
+int fault_conf_init(void);
+
+#endif
diff --git a/sys/fault/syscfg.yml b/sys/fault/syscfg.yml
new file mode 100644
index 0000000..dcf4fea
--- /dev/null
+++ b/sys/fault/syscfg.yml
@@ -0,0 +1,39 @@
+#
+# 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.
+#
+
+syscfg.defs:
+    FAULT_CLI:
+        description: >
+            Enables the `fault` CLI command.
+        value: 0
+
+    FAULT_SYSINIT_STAGE:
+        description: >
+            Sysinit stage for fault functionality.
+        value: 51
+
+    FAULT_MAX_DOMAINS:
+        description: >
+            The maximum number of fault domains that can be registered.
+        value: 8
+
+    FAULT_DOMAIN_NAMES:
+        description: >
+            Enables user friendly names for fault domains.
+        value: 0


[mynewt-core] 01/02: sys/sys: DEBUG_ASSERT()

Posted by cc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ccollins pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-core.git

commit 06cefe8e5f37600a518e79423cf8b17100d35d18
Author: Christopher Collins <cc...@apache.org>
AuthorDate: Mon Nov 5 13:57:50 2018 -0800

    sys/sys: DEBUG_ASSERT()
    
    This macro is like `DEBUG_PANIC()`, except it only crashes the system if
    the provided expression evaluates to false.
---
 sys/sys/include/sys/debug_panic.h | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/sys/sys/include/sys/debug_panic.h b/sys/sys/include/sys/debug_panic.h
index 1c3ef70..14a9e72 100644
--- a/sys/sys/include/sys/debug_panic.h
+++ b/sys/sys/include/sys/debug_panic.h
@@ -28,4 +28,10 @@
 #define DEBUG_PANIC()
 #endif
 
+#if MYNEWT_VAL(DEBUG_PANIC_ENABLED)
+#define DEBUG_ASSERT(expr) ((expr) ? (void)0 : OS_CRASH())
+#else
+#define DEBUG_ASSERT(expr)
+#endif
+
 #endif