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 "dfx/watchdog/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/ffrt_facade.h" 25 #include "util/slab.h" 26 namespace { 27 constexpr uint64_t VALID_TIMEOUT_MIN = 10000; 28 constexpr uint64_t VALID_TIMEOUT_MAX = 30000; 29 constexpr uint32_t CONVERT_TIME_UNIT = 1000; 30 constexpr int SEND_COUNT_MIN = 1; 31 constexpr int SEND_COUNT_MAX = 3; 32 } 33 34 namespace ffrt { 35 static std::map<uint64_t, int> taskStatusMap; 36 static std::mutex lock; 37 38 IsValidTimeout(uint64_t gid,uint64_t timeout_us)39 bool IsValidTimeout(uint64_t gid, uint64_t timeout_us) 40 { 41 // us convert to ms 42 uint64_t timeout_ms = timeout_us / CONVERT_TIME_UNIT; 43 // 当前有效的并行任务timeout时间范围是10-30s 44 if (timeout_ms >= VALID_TIMEOUT_MIN && timeout_ms <= VALID_TIMEOUT_MAX) { 45 FFRT_LOGI("task gid=%llu with timeout [%llu ms] is valid", gid, timeout_ms); 46 return true; 47 } else if (timeout_ms > 0) { 48 FFRT_LOGE("task gid=%llu with timeout [%llu ms] is invalid", gid, timeout_ms); 49 } 50 return false; 51 } 52 AddTaskToWatchdog(uint64_t gid)53 void AddTaskToWatchdog(uint64_t gid) 54 { 55 std::lock_guard<decltype(lock)> l(lock); 56 taskStatusMap.insert(std::make_pair(gid, SEND_COUNT_MIN)); 57 } 58 RemoveTaskFromWatchdog(uint64_t gid)59 void RemoveTaskFromWatchdog(uint64_t gid) 60 { 61 std::lock_guard<decltype(lock)> l(lock); 62 taskStatusMap.erase(gid); 63 } 64 SendTimeoutWatchdog(uint64_t gid,uint64_t timeout,uint64_t delay)65 bool SendTimeoutWatchdog(uint64_t gid, uint64_t timeout, uint64_t delay) 66 { 67 #ifdef FFRT_OH_WATCHDOG_ENABLE 68 // us convert to ms 69 uint64_t timeout_ms = timeout / CONVERT_TIME_UNIT; 70 FFRT_LOGI("start to set watchdog for task gid=%llu with timeout [%llu ms] ", gid, timeout_ms); 71 auto now = std::chrono::steady_clock::now(); 72 WaitUntilEntry* we = new (SimpleAllocator<WaitUntilEntry>::AllocMem()) WaitUntilEntry(); 73 // set dealyedworker callback 74 we->cb = ([gid, timeout_ms](WaitEntry* we) { 75 bool taskFinished = true; 76 { 77 std::lock_guard<decltype(lock)> l(lock); 78 if (taskStatusMap.count(gid) > 0) { 79 int sendCount = taskStatusMap[gid]; 80 if (sendCount > SEND_COUNT_MAX) { 81 FFRT_LOGE("parallel task gid=%llu send watchdog delaywork failed, the count more than %d times", 82 gid, SEND_COUNT_MAX); 83 SimpleAllocator<WaitUntilEntry>::FreeMem(static_cast<WaitUntilEntry*>(we)); 84 return; 85 } 86 taskStatusMap[gid] = (++sendCount); 87 taskFinished = false; 88 } 89 } 90 91 if (!taskFinished) { 92 RunTimeOutCallback(gid, timeout_ms); 93 if (!SendTimeoutWatchdog(gid, timeout_ms * CONVERT_TIME_UNIT, 0)) { 94 FFRT_LOGE("parallel task gid=%llu send next watchdog delaywork failed", gid); 95 SimpleAllocator<WaitUntilEntry>::FreeMem(static_cast<WaitUntilEntry*>(we)); 96 return; 97 }; 98 } else { 99 FFRT_LOGI("task gid=%llu has finished", gid); 100 } 101 SimpleAllocator<WaitUntilEntry>::FreeMem(static_cast<WaitUntilEntry*>(we)); 102 }); 103 // set dealyedworker wakeup time 104 std::chrono::microseconds timeoutTime(timeout); 105 std::chrono::microseconds delayTime(delay); 106 we->tp = (now + timeoutTime + delayTime); 107 if (!DelayedWakeup(we->tp, we, we->cb, true)) { 108 SimpleAllocator<WaitUntilEntry>::FreeMem(we); 109 FFRT_LOGE("failed to set watchdog for task gid=%llu with timeout [%llu ms] ", gid, timeout_ms); 110 return false; 111 } 112 #endif 113 return true; 114 } 115 RunTimeOutCallback(uint64_t gid,uint64_t timeout)116 void RunTimeOutCallback(uint64_t gid, uint64_t timeout) 117 { 118 #ifdef FFRT_OH_WATCHDOG_ENABLE 119 std::stringstream ss; 120 ss << "parallel task gid=" << gid << " execution time exceeds " << timeout << " ms"; 121 std::string msg = ss.str(); 122 FFRT_LOGE("%s", msg.c_str()); 123 124 if (ffrt_task_timeout_get_cb()) { 125 FFRTFacade::GetDWInstance().SubmitAsyncTask([gid, msg] { 126 ffrt_task_timeout_cb func = ffrt_task_timeout_get_cb(); 127 if (func) { 128 func(gid, msg.c_str(), msg.size()); 129 } 130 }); 131 } 132 #endif 133 } 134 }