最近花了一周時間對場景服務進行熱點分析,利用以前的火焰圖工具做了一點微小的貢獻,分享下心得(倉庫地址在https://github.com/spin6lock/skynet_systemtap_set)。
Skynet是一個輕量級的多線程在線游戲框架。線程作為worker,從服務的隊列中抽出有消息的,然后處理上面的消息。服務間通過發消息來相互通信。目前服務主要是用Lua進行編寫。
以前做的火焰圖工具,沒法單獨看一個skynet服務的堆棧,只能看到整個進程的。要想針對單個服務做優化分析,需要抓出skynet_context的地址,才能取到服務的id。一個服務對應的是一個Lua VM,在這個VM里,snlua.so會悄悄的將skynet_context地址塞進去REGISTRY。那么,我們只要將skynet_context取出來,然后判斷skynet_context->handle即可。這看似簡單的東西,在C里就一句話:
struct skynet_context * ctx = lua_touserdata(L, lua_upvalueindex(1));
但是,翻譯成stap腳本后,就變成了:
if (((((p->func))->tt_) == ((6 | (1 << 4))))) { next } else { gc = p->func->value_->gc closure = &@cast(gc, "GCUnion", @1)->cl func = &@cast(closure, "Closure", @1)->c //CClosure if (idx <= func->nupvalues) { upvalue_type = func->upvalue[idx - 1]->tt_ if (upvalue_type != 2) { next } /* only this one can enter counter */ skynet_context = &@cast(func->upvalue[idx - 1]->value_->p, "skynet_context", @1) /* service id in decimal*/ if (skynet_context->handle != strtol(@2, 10)) { next } } else { next } }
感謝gcc -E,幫我展開了一重重的宏……
這個過程無比蛋疼,要將C翻譯成stp腳本。stp可以保護新手,避免搞掛內核,但是隨之而來的就是晦澀的指針運算魔法了。接下來打算試試用bcc寫了
Anyway,折騰出來一個火焰圖:
火焰圖取材於在內核取樣的數據點,縱向切一刀,表示當前時刻的lua棧。橫向表示統計時間內,各個棧幀的占比。橫向越長,時間越長。縱向越深,調用越深。顏色沒有關系,可以忽略
可以看到里面有個堆棧很高,說明調用深度很深,另外寬度也很大,說明調用頻次占比高。后來定位了一下這個函數,發現是多調了一次aoi同步……
優化后棧的深度變淺了,剩下的基本是壓測腳本跑的內容了。棧深度還能優化一下,到C函數的調用層次不宜過多。
好了,睡覺了……