透過 WinDbg 來找出 ASP.NET CPU 100% ASP.NET 程式的問題

問題

我們有一個 ASP.NET 的系統部署到 IIS 上(Windows 2012, .NET 4.x),有時候會導致 w3wp.exe 吃掉所有的 CPU 資源,一直要等到應用程式回收後,程式再重新啟動後就正常了。這種狀況不定期會發生。

解法

我們可以使用 WinDbg 來找出導致 IIS CPU 100% 的原因,方法如下,

  • 透過「工作管理員」來「建立傾印檔案」
    當發生 IIS 導致 CPU 100% 時,開啟「工作管理員」,按右鍵選取「建立傾印檔案」。要依 Web 應用程式的平台(x86/x64)來開啟「工作管理員」(x86的工作管理員在 C:\Windows\SysWOW64\taskmgr.exe )。
  • 安裝 WinDbg
    請參考 Debugging Tools for Windows (WinDbg, KD, CDB, NTSD) 來安裝對應的版本。
  • 設定 Symbol Path
    先建立一個 C:\RTX64_SYMBOLS 目錄(依您自行定義),然後開啟 Command 設定 _NT_SYMBOL_PATH 的環境變數,如下,

    set _NT_SYMBOL_PATH=srv*C:\RTX64_SYMBOLS*https://msdl.microsoft.com/download/symbols

    set _NT_SYMBOL_PATH所以在 command 中,輸入 set 後,就可以看到 _NT_SYMBOL_PATH 的設定值哦,

    _NT_SYMBOL_PATH當然,如果常常會用到的話,就直接設定到 環境變數之前,下次 debug 時,就不用再設定一次了哦! 詳細請參考Set up your system to use Microsoft’s public Symbol Server

  • 在 WinDbg 中找問題
    • 載入 sos.dll
      WinDbg 有 x86/x64 的版本,我是使用 x64 的版本,所以 sos.dll 的路徑是 C:\Windows\Microsoft.NET\Framework64\v4.0.30319\sos.dll,您輸入如果發生錯誤的話,請使用 C:\Windows\Microsoft.NET\Framework\v4.0.30319\sos.dll 。
    • 設定 Symbol 檔目錄
      !sym noisy
      .cordll -ve -u -l
    • 透過 !runaway 顯示Threads所佔的時間
      輸入 !runaway 後,會顯示各 Thread 所花費的時間,花最多的會在最上面,如下圖,

      !runaway

      !runaway

    • 透過 ~[thread]s 切換到該 thread 的位置
      由上圖所示,Thread 40 佔最多時間,所以我們切到它的位罝去,

      ~40s

      ~40s

    • 透過 !clrstack 來查看呼叫堆疉
      !clrstack

      !clrstack從上圖可以發現,應該是有關 Dictionary 操作的問題,而它是由我們系統中 TemplateCfg.initial() 這個 Method 所引起的。

  • 檢視並調整程式碼
    開啟 TemplateCfg 程式碼,在 initial 這個 Method 之中會建立 Dictionary 物件,Clear 它,並 Insert 資料,但這些 Dictionary 的變數卻又設定成 static 。TemplateCfg.initial()即然這些 Dictionary 是 static 的,而且它們值又都是相同的,就沒有必要每次 request 時,就重新建立並 insert 這些資料。
    所以將 initial 裡面的 Code 搬到 static constructor 。
    調整完程式碼後,從去年觀察到目前,已經沒有再發生 CPU 100% 的狀況。

參考資料

Debugging in Production Part 1 – Analyzing 100% CPU Usage Using Windbg
WinDBG 應用實例:找出 ASP.NET CPU 100% 原因
Debugging Tools for Windows (WinDbg, KD, CDB, NTSD)
Set up your system to use Microsoft’s public Symbol Server
High CPU Hangs – 05
中小型研发团队架构实践:生产环境诊断利器WinDbg帮你快速分析异常情况Dump文件
Intro to WinDBG for .NET Developers
.NET Debugging Demos Lab 4: High CPU Hang – Review

如何在.Net專案中開始使用TypeScript

在介紹如何在.Net專案中開始使用TypeScript之前先來講講,在使用JavaScript時曾經碰過的幾個問題:

1. JavaScript沒有OO
若要讓JavaScript有OO的特性就必須透過Protype來達成,但問題是需要多輸入一些程式碼,寫法如下圖所示:

2.誤用ES6或是ES7的語法
在開發JavaScript有時不會特別留意是使用哪一個版本新增的語法,如果不小心用到比較新的語法時,會讓舊版本的瀏覽器 (如:IE) 無法運行。

<下圖> Foo2 為ES6新增的寫法,程式碼雖然精簡,但是在IE上是無法運行,因為IE不支援ES6以上的語法。

3.只有在偵錯時才知道有沒有語法錯誤
是用JavaScript開發時,往往都要在瀏覽器執行時才會知道這段JS有沒有語法錯誤。
在用TypeScript開發時,在編譯的階段就可以知道語法有沒有錯誤,可以大幅減少除錯的時間。

<下圖>   IDE在編譯之前就已經抓到錯誤的語法:


接下來介紹要如何在.Net專案設定TypeScript
Step 1.開發環境

(1) TypeScript
TypeScript安裝途徑有兩個:
● 安裝Visual Studio時順便安裝。
● Visual Studio擴充功能 → 搜尋TypeScript → 下載並安裝。

(2) Visual Studio版本:Visual Studio 2017、Visual Studio 2015、Visual Studio 2013 (不確定是否可以)

Step 2.新增tsconfig.json (TypeScript設定檔)

專案根目錄 → 新增新項目 → TypeScript設定檔

Step 3.編輯tsconfig.json

如果要將所有的TS檔編譯成1個JS檔,可以將下圖”outDir”換成”outFile”,後面的目錄加上JS的檔名。
tsconfig的屬性有很多就不全部介紹了,有興趣的話可以到官網看看

Step 4.加入TS檔,並開始開發

TS檔編輯完後存檔後會在專案目錄下可以看到JS檔和Map檔,這樣就大功告成了。
注意:當TS檔有錯誤時,不會編譯JS檔和Map檔


在開發Typescript時難免會引用外部的JavaScript套件,但是在外部的JavaScript套件會遇到一個問題,那就是在使用外部JS的物件時,會出現錯誤訊息,如下圖所示:
這個問題的解法上在TS加入紅色圈起的程式碼後,錯誤訊息就消失了,問題也就排除了。

最後,提供一下TypeScript學習的資源,希望各位能夠順利開發。

TypeScript官方文件 (英文)
HackMD – TypeScript新手入門 (正體中文)
GitBook – TypeScript 入門教學 (簡體中文)

測試隨筆-超好用的 Selenium 內建 Action Class

用 Action Class 解決了很多問題:

    • 問題:在 contenteditable = true 中使用 SendKeys 會有錯誤訊息:Cannot focus web element to send keys 。
      不需要用 JavaScript 的方式來處理,簡單很多。
      網路上蒐到使用 JavaScript 的解法比較多,但可以用 Action 簡單處理完成。
 原始 html 碼
<ul data-id=”xxx” contenteditable=”ture”>
<li data-id=”yyy:”>
<li data-id=”xxx:”>
 driver.FindElement(By.XPath(“locator”)).SendKeys(“xxx”);
→ 會有錯誤訊息:Cannot focus web element to send keys
 IWebElement visibleInput = driver.FindElement(By.XPath(locator));
IJavaScriptExecutor executor = (IJavaScriptExecutor)driver;
executor.ExecuteScript(“arguments[0].click();”, visibleInput);
driver.FindElement(By.XPath(locator)).SendKeys(“xxx”);
→ 目前遇到是無法把字輸入成功的。
 IWebElement element = driver.FindElement(By.XPath(locator));
Actions action = new Actions(driver);
action.MoveToElement(element).Click().Perform();
action.MoveToElement(element).SendKeys(“xxx”).Perform();
→ 可以運作成功, 有 innerHTML 的也可以用 Action 簡單處理。
  • 問題:沒有 DoubleClick 可以選時Action 也支援 Mouse、Keyboard 事件。
 driver.FindElement(By.XPath(locator).Click();
這裡沒有 doubleClick method 可以選。
 action.MoveToElement(element).DoubleClick().Perform();
承上所述,直接使用 DoubleClick() method
  • 問題:IE11 的這種錯誤【error: Cannot click on element (WARNING: The server did not provide any stacktrace information) 】(相同腳本 Chrome 是可以跑的),
    改用 Action 簡單處理。(舉例同第一項)
  • 要引用 using OpenQA.Selenium.Interactions;

參考:

Angular 4 folders-by-feature structure practice

Angular 4 切版實戰 part 1: 新手上路甘苦談

註:未來 AngularJS (1.x) 後的版本統稱 Angular,不過為了方便理解暫且稱為 Angular 4,而且現在 Google “Angular” 的資訊也大多還是在講 AngualrJS

雖然字面上是甘苦談,但就是苦談的意思。之後可能會變成系列文,當作開發過程中的心得整理。

寫 AngularJS (以下簡稱 A1) 習慣之後,現在寫 Angular 4 (以下簡稱 A4) 有種打掉重練的感覺。尤其看著寫過 VueJS 的夥伴 Eric Yip 迅速寫完一堆 A4 的 component 和 service ,更加充滿徬徨。

繼續閱讀 “Angular 4 folders-by-feature structure practice”

無法載入檔案或組件 ‘log4net, Version=1.2.10.0, Culture=neutral, PublicKeyToken=692fbea5521e1304’ 或其相依性的其中之一

最近同事詢問他們使用 Crytal Report Viewer 時,會發生 log4net 版本衝突的問題。如下圖所示,
CrystalReportViewer Runtime Error

如果是不同版本衝突的話,可以參考 如何讓不同 PublicKeyToken 的 DLL assemblyBinding 到可以用版本? 透過 assemblyBinding 的方式就可以解決。 但剛好我們的 AP 自已用的 log4net 版本也是 1.2.10.0,所以是 相同版本,不同的 PublickToken 的衝突。 詳細訊息如下,

沒有辦法解決 “log4net, Version=1.2.10.0, Culture=neutral, PublicKeyToken=1b44e1d426115821” 和 “log4net, Version=1.2.10.0, Culture=neutral, PublicKeyToken=692fbea5521e1304” 之間的衝突。
任意選擇 “log4net, Version=1.2.10.0, Culture=neutral, PublicKeyToken=1b44e1d426115821”。

有查到 Error: Could not load log4net assembly 似乎是因為不同的平台(x86/x64) 所用的 log4net 會不同。
x86(32位元)用的 PublicToken 是 692fbea5521e1304,x64(64位元)或是 AnyCPU 用的 PublicToken 則是 1b44e1d426115821。
而我們 ap 平台是 x64 的,但 Crystal Report Viewer 卻使用了 x86(32位元) 的 log4net 。

嗯… 我想應該是那裡設定出了錯吧 …

跟同事討論後,同事又說只有在某台機器上才會出現那個錯誤,如果是透過 Browser 直接使用系統就正常,透過 VS.NET 啟動程式去 Debug 時,就會出現那個錯誤。
我想是因為如果透過 Browser 去使用系統的話,是連到 IIS ,而 IIS 設定的是 x64(64位元);如果透過 VS.NET 則是連到 IIS Express,預設它是使用 x86(32位元)。
於是筆者將 IIS 上的應用程式集區設定成 啟用 32 位元,再執行程式,則一樣會出現 log4net 的衝突狀況(  重現問題了 …^_^)。
所以就請同事在 VS.NET 中設定讓 IIS Express 使用 64 位元就沒問題了哦!
工具 -> 選項 -> (search textbox 中輸入 iis) , 並勾選 將 64 位元版本的 IIS Express 用於網站和專案(U) 選項勾起來就可以了哦! 如下,
將 64 位元版本的 IIS Express 用於網站和專案(U)

如果您是系統出了錯,請 Check 應用程式集區是否設定成了 「啟用32位元應用程式」哦!