1 /*
2  * Copyright (c) 2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #include "watchdog_util.h"
16 #include <sstream>
17 #include <algorithm>
18 #include <map>
19 #include "sync/sync.h"
20 #ifdef FFRT_OH_WATCHDOG_ENABLE
21 #include "c/ffrt_dump.h"
22 #endif
23 #include "dfx/log/ffrt_log_api.h"
24 #include "util/slab.h"
25 namespace {
26 constexpr uint64_t VALID_TIMEOUT_MIN = 10000;
27 constexpr uint64_t VALID_TIMEOUT_MAX = 30000;
28 constexpr uint32_t CONVERT_TIME_UNIT = 1000;
29 constexpr int SEND_COUNT_MIN = 1;
30 constexpr int SEND_COUNT_MAX = 3;
31 }
32 
33 namespace ffrt {
34     static std::map<uint64_t, int> taskStatusMap;
35     static std::mutex lock;
36 
37 
IsValidTimeout(uint64_t gid,uint64_t timeout_us)38     bool IsValidTimeout(uint64_t gid, uint64_t timeout_us)
39     {
40         // us convert to ms
41         uint64_t timeout_ms = timeout_us / CONVERT_TIME_UNIT;
42         // 当前有效的并行任务timeout时间范围是10-30s
43         if (timeout_ms >= VALID_TIMEOUT_MIN && timeout_ms <= VALID_TIMEOUT_MAX) {
44             FFRT_LOGI("task gid=%llu with timeout [%llu ms] is valid", gid, timeout_ms);
45             return true;
46         } else if (timeout_ms > 0) {
47             FFRT_LOGE("task gid=%llu with timeout [%llu ms] is invalid", gid, timeout_ms);
48         }
49         return false;
50     }
51 
AddTaskToWatchdog(uint64_t gid)52     void AddTaskToWatchdog(uint64_t gid)
53     {
54         std::lock_guard<decltype(lock)> l(lock);
55         taskStatusMap.insert(std::make_pair(gid, SEND_COUNT_MIN));
56     }
57 
RemoveTaskFromWatchdog(uint64_t gid)58     void RemoveTaskFromWatchdog(uint64_t gid)
59     {
60         std::lock_guard<decltype(lock)> l(lock);
61         taskStatusMap.erase(gid);
62     }
63 
SendTimeoutWatchdog(uint64_t gid,uint64_t timeout,uint64_t delay)64     bool SendTimeoutWatchdog(uint64_t gid, uint64_t timeout, uint64_t delay)
65     {
66 #ifdef FFRT_OH_WATCHDOG_ENABLE
67         // us convert to ms
68         uint64_t timeout_ms = timeout / CONVERT_TIME_UNIT;
69         FFRT_LOGI("start to set watchdog for task gid=%llu with timeout [%llu ms] ", gid, timeout_ms);
70         auto now = std::chrono::steady_clock::now();
71         WaitUntilEntry* we = new (SimpleAllocator<WaitUntilEntry>::AllocMem()) WaitUntilEntry();
72         // set dealyedworker callback
73         we->cb = ([gid, timeout_ms](WaitEntry* we) {
74             std::lock_guard<decltype(lock)> l(lock);
75             if (taskStatusMap.count(gid) > 0) {
76                 RunTimeOutCallback(gid, timeout_ms);
77             } else {
78                 FFRT_LOGI("task gid=%llu has finished", gid);
79             }
80             SimpleAllocator<WaitUntilEntry>::FreeMem(static_cast<WaitUntilEntry*>(we));
81         });
82         // set dealyedworker wakeup time
83         std::chrono::microseconds timeoutTime(timeout);
84         std::chrono::microseconds delayTime(delay);
85         we->tp = (now + timeoutTime + delayTime);
86         if (!DelayedWakeup(we->tp, we, we->cb)) {
87             SimpleAllocator<WaitUntilEntry>::FreeMem(we);
88             FFRT_LOGE("failed to set watchdog for task gid=%llu with timeout [%llu ms] ", gid, timeout_ms);
89             return false;
90         }
91 #endif
92         return true;
93     }
94 
RunTimeOutCallback(uint64_t gid,uint64_t timeout)95     void RunTimeOutCallback(uint64_t gid, uint64_t timeout)
96     {
97 #ifdef FFRT_OH_WATCHDOG_ENABLE
98         std::stringstream ss;
99         ss << "parallel task gid=" << gid << " execution time exceeds " << timeout << " ms";
100         std::string msg = ss.str();
101         FFRT_LOGE("%s", msg.c_str());
102         ffrt_task_timeout_cb func = ffrt_task_timeout_get_cb();
103         if (func) {
104             func(gid, msg.c_str(), msg.size());
105         }
106         int sendCount = taskStatusMap[gid];
107         if (sendCount >= SEND_COUNT_MAX) {
108             FFRT_LOGE("parallel task gid=%llu send watchdog delaywork failed, the count more than the max count", gid);
109             return;
110         }
111         if (!SendTimeoutWatchdog(gid, timeout * CONVERT_TIME_UNIT, 0)) {
112             FFRT_LOGE("parallel task gid=%llu send next watchdog delaywork failed", gid);
113             return;
114         };
115         taskStatusMap[gid] = (++sendCount);
116 #endif
117     }
118 }
119