日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

[原]调试实战——程序CPU占用率飙升,你知道如何快速定位吗?

發(fā)布時間:2023/12/4 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [原]调试实战——程序CPU占用率飙升,你知道如何快速定位吗? 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

前言

如果我們自己的程序的CPU Usage(CPU占用率)飆升,并且居高不下,很有可能陷入了死循環(huán)。你知道怎么快速定位并解決嗎?今天跟大家分享幾種定位方法,希望對你有所幫助。

如何判斷是否有死循環(huán)?

  • 通過電腦風扇的聲音猜測。

    如果風扇一直響個不停,說明電腦很熱。高CPU占用率會導致CPU發(fā)熱量增大,從而導致風扇狂響。如果聽到風扇響個不停,可以打開任務管理器看看CPU占用率是不是很高。如果發(fā)現(xiàn)是我們的進程導致的高CPU占用率,那么可以進一步查看是不是有死循環(huán)。

  • 通過CPU占用率來判斷。

    對于多核CPU(尤其是性能強勁的CPU),一個核心的滿負荷運轉(zhuǎn),并不會立刻導致CPU發(fā)熱量明顯增大,風扇可能不會有明顯響動。這時根據(jù)風扇聲音不能輕易判斷出是否有死循環(huán),但是我們可以通過CPU占用率來判斷。

    如果CPU是單核的,那么當CPU處于滿負荷運轉(zhuǎn)狀態(tài),CPU占用率會接近100%。如果CPU是4核的,并且這4個核心都處于滿負荷運轉(zhuǎn)狀態(tài),那么CPU占用率會接近100%,如果只有一個核心是滿負荷運轉(zhuǎn)狀態(tài),那么CPU占用率會在25%(100 / 4 = 25)左右。如果我們發(fā)現(xiàn)某個進程的CPU占用率居高不下,有可能是死循環(huán)了。

    注意: 很多死循環(huán)都是busy類型的,如果是idle類型的死循環(huán),上面的方法不適用。

下面介紹幾個我經(jīng)常使用的工具,可以比較便捷的排查此類問題。

1. process explorer

在前面的文章里跟大家介紹過,使用process explorer可以查看線程的調(diào)用棧及CPU占用率。如果程序里的某個功能遲遲不能完成,我的第一反應是,按Ctrl + Shift + Esc打開任務管理器(我已經(jīng)使用process explorer替換了系統(tǒng)自帶的任務管理器,所以啟動的是process explorer。如何使用process explorer替換系統(tǒng)自帶的任務管理器,請參考文章[原]排錯實戰(zhàn)——使用process explorer替換任務管理器)。

啟動process explorer后,雙擊我們關(guān)心的進程,切換到Thread頁,在這里我們可以看到當前進程中的所有線程。雙擊某個線程就可以查看調(diào)用棧,在彈出的調(diào)用棧界面,點擊左下角的Refresh按鈕可以刷新。

如果每次刷新都能看到某個函數(shù),很有可能是在這個函數(shù)中出現(xiàn)了死循環(huán)。對照源碼,也許能直接能看出原因。

使用process explorer

注意: 需要正確加載調(diào)試符號才可以看到對應的函數(shù)名。

2. windbg

如果不能使用process explorer定位到具體的原因,可以使用windbg附加到進程中進行更深入的調(diào)查。我們需要找出哪個線程運行的時間最長,因為一般死循環(huán)的線程占用的CPU時間會比較長。應該怎么找呢?????

  • 使用.ttime命令

    .ttime可以查看當前線程的運行時間(用戶態(tài)運行時間和內(nèi)核態(tài)運行時間)。但是.ttime有個不足之處——沒有輸出相關(guān)的線程標識。我們需要根據(jù)其它信息來獲取當前線程的標識。

    如果想查看所有線程的運行時間怎么辦呢?當然可以手動切換到另外一個線程,然后執(zhí)行.ttime。如果線程數(shù)量很多的話,這可是個體力活。不要怕,我們可以通過命令~*e .ttime來獲取每個線程的運行時間。因為.ttime輸出結(jié)果中沒有線程標識,我們需要執(zhí)行命令 ~*e ? $tid;.ttime 把對應的線程ID一起輸出。

    獲取所有線程ID和運行時間

    簡單向大家解釋下這條命令:

    • ~*e會遍歷所有線程并執(zhí)行后面跟著的命令。其實,~*就可以遍歷所有線程,比如我們在前面的文章里用到的~* kvn命令來查看所有線程的調(diào)用棧。但是對于某些命令,如果不加e,windbg可能不能正確解析,會報錯。

    • ? $tid評估表達式$tid的值,?在windbg中表示Evaluate的意思,會評估后面表達式的值。$tid是偽變量,代表了當前線程的線程ID。

    • ; 分號是命令分割符。

    • .ttime查看當前線程的運行時間。

整條命令的效果是:遍歷每個線程,輸出其對應的線程ID和運行時間。

  • 如果覺得上面的命令太長了,還可以使用更簡單的命令!runaway查看線程運行時間。

下面是我用!runaway命令排查高CPU占用率的屏幕錄像。

3. visual studio

如果是正在開發(fā)的程序在運行過程中出現(xiàn)了死循環(huán),我會考慮用vs來附加到進程(如果進程是通過Ctrl + F5啟動的話,并沒有被調(diào)試)。然后通過Parallel Stacks查看所有線程,并用肉眼查找可能出問題的線程。因為我不知道vs中是否有類似!runaway的命令。如果哪位小伙伴有更好的辦法,請一定要留言告訴我!

下面是我用Parallel Stacks功能排查高CPU占用率的屏幕錄像。

小提示: 按Ctrl + Alt + p可以快速打開附加進程界面。

小結(jié)

以上三種工具,我會先使用process explorer大體定位下問題,因為可以非常方便的通過Ctrl + Shift + Esc啟動。如果用process explorer解決不了,我會根據(jù)情況使用windbg或者vs。如果vs正開著(通常是正在寫代碼的時候),就順手用vs附加到對應的進程中。如果vs沒開著,當然會使用windbg進行排查了。????

實戰(zhàn)代碼

如果你想動手實戰(zhàn),復制下面的代碼到工程里就可以實戰(zhàn)了。

簡單介紹下代碼:

  • 示例代碼中啟動了8個線程,是為了增大排查的難度,只有一個線程的情況太簡單了。

  • 函數(shù)FindFirstRepeatElementIndex()的用途是找到給定的數(shù)據(jù)中第一次出現(xiàn)重復的數(shù)據(jù)的索引

  • 除了我們發(fā)現(xiàn)的死循環(huán)的問題,還有什么地方可以優(yōu)化呢?命名,效率,各個方面都可以優(yōu)化哦,歡迎留言交流。

#include <vector> #include <future> #include <iostream>int FindFirstRepeatElementIndex(bool bExcute) {if (!bExcute){return -1;}int idx = -1;std::vector<int> datas = { 1 , 3, 5, 7, 9, 11, 11, 13, 14, 15, 16, 17 };for (size_t i = 0; i < datas.size(); ++i){for (size_t j = i = 1; j < datas.size(); ++j){if (datas[j] == datas[i]){idx = i;break;}}}return idx; }#define THREAD_COUNT 8 int main() {std::future<int> results[THREAD_COUNT];int realExcuteIdx = rand() % THREAD_COUNT;for (int idx = 0; idx < THREAD_COUNT; ++idx){bool bRealExcute = (realExcuteIdx == idx);results[idx] = std::async(FindFirstRepeatElementIndex, bRealExcute);}for (auto& one_result : results){std::cout<< one_result.get() << std::endl;}return 0; }

總結(jié)

  • 使用process explorer的線程相關(guān)功能,在某些情況下,我們甚至可以不用調(diào)試器,對照源碼就可以找出問題所在。

  • visual studio的并行調(diào)用棧可以讓我們一次性看到所有線程的調(diào)用棧,很是方便。不像Call Stack,每次只能查看一個線程的調(diào)用棧。當然除了看所有線程的調(diào)用棧,還有更多用途等待大家挖掘。

  • 一般,如果一個線程的運行時間大于其它線程,這個線程很有可能是與死循環(huán)相關(guān)的線程。

  • windbg的!runaway命令可以查看每個線程運行的時間,運行時間最長的線程會排在第一位。

  • ~*e ? $tid;.ttime可以查看所有線程的運行時間。

  • ~Ns切換到第N號線程。

  • ~~[TID]s 切換到TID對應的線程。

參考資料

  • 《格蠹匯編》

  • 《Windows Sysinternals 實戰(zhàn)指南》

猜你喜歡:

[原]調(diào)試實戰(zhàn)——使用windbg調(diào)試崩潰在ole32!CStdMarshal::DisconnectSrvIPIDs

[原]調(diào)試實戰(zhàn)——崩潰在ComFriendlyWaitMtaThreadProc

[原]調(diào)試實戰(zhàn)——調(diào)試PInvoke導致的內(nèi)存破壞

[原]調(diào)試實戰(zhàn)——調(diào)試excel啟動時死鎖

[原]調(diào)試實戰(zhàn)——調(diào)試TerminateThread導致的死鎖

[原]調(diào)試實戰(zhàn)——調(diào)試DLL卸載時的死鎖

[原]排錯實戰(zhàn)——拯救加載調(diào)試符號失敗的IDA

[原]排錯實戰(zhàn)——使用process explorer替換任務管理器

[原]排錯實戰(zhàn)——VS清空最近打開的工程記錄

[原]排錯實戰(zhàn)——解決Tekla通過.tsep安裝插件失敗的問題

[原]排錯實戰(zhàn)——你知道拖動窗口時只顯示虛框怎么設(shè)置嗎?

[原]排錯實戰(zhàn)——通過對比分析sysinternals事件修復程序功能異常

你知道怎么使用DebugView查看調(diào)試信息嗎?

歡迎留言交流

總結(jié)

以上是生活随笔為你收集整理的[原]调试实战——程序CPU占用率飙升,你知道如何快速定位吗?的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。