记一次 .NET某游戏后端API服务 CPU爆高分析
一:背景
1. 讲故事
前几天有位朋友找到我,说他们的API服务程序跑着跑着CPU满了降不下去,让我帮忙看下怎么回事,现在貌似民间只有我一个人专注dump分析,还是申明一下我dump分析是免费的,如果想学习.NET高级调试的分析技术,可以来我的训练营看看,话不多说,dump分析走起!
二:WinDbg分析
1. CPU 真的爆高吗
昨天录了一个小视频,大意就是作为分析师,不要过分的相信客户说的话,他们往往会把你带偏,你要有自己的分析思路和前进方向,所以需要以数据说话,首先用 !tp
观察下线程池。
0:030> !tp
CPU utilization: 60%
Worker Thread: Total: 18 Running: 3 Idle: 15 MaxLimit: 32767 MinLimit: 4
Work Request in Queue: 0
--------------------------------------
Number of Timers: 3
--------------------------------------
Completion Port Thread:Total: 3 Free: 1 MaxFree: 8 CurrentLimit: 1 MaxLimit: 1000 MinLimit: 4
从卦中可以看到当前的 CPU=60%
,这个值说高也不高,说低也不低,接下来观察下这台机器的cpu核心数,可以用 !cpuid
观察。
0:030> !cpuid
CP F/M/S Manufacturer MHz
0 6,63,2 <unavailable> 2295
1 6,63,2 <unavailable> 2295
2 6,63,2 <unavailable> 2295
3 6,63,2 <unavailable> 2295
真的是无语,做游戏的不都是有钱的主,难道都在降本增效吗?既然到了 60%,说明有两个线程估计脱轨了,接下来就需要观察下那2个托轨线程都在做什么?
2. 脱轨线程在干嘛
要想观察每个线程都在做什么,可以使用 ~*e !clrstack
命令即可。
0:030> ~*e !clrstack
OS Thread Id: 0x3eec (30)
Child SP IP Call Site
0000001f8fbad610 00007ffd958535c3 System.Collections.Generic.HashSet`1[[System.__Canon, mscorlib]].Contains(System.__Canon)
0000001f8fbad680 00007ffd95372933 System.Web.HttpCookieCollection.EnsureKeyValidated(System.String, System.String)
0000001f8fbad6c0 00007ffd9483fa8d System.Web.HttpCookieCollection.Get(System.String)
0000001f8fbad700 00007ffd3d12b3da xxx.CookieHelper.Read(System.String)
...
OS Thread Id: 0x5cf0 (31)
Child SP IP Call Site
0000001f8d27d330 00007ffd958535b8 System.Collections.Generic.HashSet`1[[System.__Canon, mscorlib]].Contains(System.__Canon)
0000001f8d27d3a0 00007ffd95372933 System.Web.HttpCookieCollection.EnsureKeyValidated(System.String, System.String)
0000001f8d27d3e0 00007ffd9483fa8d System.Web.HttpCookieCollection.Get(System.String)
0000001f8d27d420 00007ffd3e2ab6da xxx.CookieHelper.Read(System.String)
...
仔细琢磨了一下卦象后,发现有两个线程都停在 HashSet
上,而且这个集合还是来自于底层的 System.Web
,对一般人来说这个是比较奇葩的现象,但对于我这种有300+分析经验的熟手来说,一眼就看出来了什么问题,对,就是多线程操控 HashSet 导致的死循环,接下来的问题是如何去验证呢?毕竟空口无凭。。。
3. 真的死循环了吗
要想了解有没有真的死循环,需要你对 HashSet 的底层有一个了解,比如说 HashSet 挂链的时候在内部是如何组织的,其实这个我在.NET高级训练营
里面也做过讲解,也做过演示,接下来切到 31 号线程观察下它的 HashSet 结构。
0:031> !mdso
Thread 31:
Location Object Type
------------------------------------------------------------
RCX: 000000200054ffc0 System.Collections.Generic.HashSet`1+Slot[[System.String, mscorlib]][]
RSI: 000000200054ff58 System.Collections.Generic.HashSet`1[[System.String, mscorlib]]
...
0:031> !mdt 000000200054ff58
000000200054ff58 (System.Collections.Generic.HashSet`1[[System.String, mscorlib]])
m_buckets:000000200054ff98 (System.Int32[], Elements: 3)
m_slots:000000200054ffc0 (System.Collections.Generic.HashSet`1+Slot[[System.String, mscorlib]][], Elements: 3, ElementMT=00007ffd95555230)
m_count:0x0 (System.Int32)
m_lastIndex:0x0 (System.Int32)
m_freeList:0xffffffff (System.Int32)
m_comparer:00000022003d3380 (System.OrdinalComparer)
m_version:0x6 (System.Int32)
m_siInfo:NULL (System.Runtime.Serialization.SerializationInfo)
...
0:031> !mdt -e:2 000000200054ffc0
000000200054ffc0 (System.Collections.Generic.HashSet`1+Slot[[System.String, mscorlib]][], Elements: 3, ElementMT=00007ffd95555230)
[0] (System.Collections.Generic.HashSet`1+Slot[[System.String, mscorlib]]) VALTYPE (MT=00007ffd95555230, ADDR=000000200054ffd0)
hashCode:0xffffffff (System.Int32)
next:0x0 (System.Int32)
value:NULL (System.__Canon)
[1] (System.Collections.Generic.HashSet`1+Slot[[System.String, mscorlib]]) VALTYPE (MT=00007ffd95555230, ADDR=000000200054ffe0)
hashCode:0x3eb5808c (System.Int32)
next:0xffffffff (System.Int32)
value:000000200054f8f0 (System.String) Length=6, String="xxx"
[2] (System.Collections.Generic.HashSet`1+Slot[[System.String, mscorlib]]) VALTYPE (MT=00007ffd95555230, ADDR=000000200054fff0)
hashCode:0x7e225883 (System.Int32)
next:0x1 (System.Int32)
value:000000200054fba0 (System.String) Length=12, String="xxx"
熟悉 HashSet底层的朋友,从上面的卦信息一眼就能看出问题,对,就是这个 next:0x0
,在hashset的挂链中,最后一个节点永远是 -1,如果是 0 的话就相当于指向数组的首元素,最后就是无情死循环了,知道了前因后果之后,接下来就要寻找下到底是什么圣神代码。
4. 到底是什么奇葩代码
这个比较简单,观察下线程栈的托管层代码,然后看源码即可,为了保护客户隐私,我就多注释一点,输出如下:
0:031> !clrstack
OS Thread Id: 0x5cf0 (31)
Child SP IP Call Site
0000001f8d27d330 00007ffd958535b8 System.Collections.Generic.HashSet`1[[System.__Canon, mscorlib]].Contains(System.__Canon)
0000001f8d27d3a0 00007ffd95372933 System.Web.HttpCookieCollection.EnsureKeyValidated(System.String, System.String)
0000001f8d27d3e0 00007ffd9483fa8d System.Web.HttpCookieCollection.Get(System.String)
0000001f8d27d420 00007ffd3e2ab6da xxx.CookieHelper.Read(System.String)
...
虽然信息比较少,但卦还是很明朗的,客户写了一个 CookieHelper 封装了 Request.Cookies 操作,那到底怎么封装的呢?仔细阅读代码之后终于发现了,截图如下:
我去,这代码还是挺奇葩的,居然将 Cookies 给了静态变量,静态变量可是一个进程小缓存呢,水落石出之后改发也比较简单,把 static 去掉即可。
三:总结
这种将Request.Cookies
赋给静态变量的奇葩操作其实蕴含着巨大的安全隐患,会导致多个用户之间串cookie,但以服务器自爆的方式来避免客户端串cookie,真的是不幸中的万幸,哈哈,同时用bug去抑制另一个bug的神操作也真的是大自然的鬼斧神工!
记一次 .NET某游戏后端API服务 CPU爆高分析的更多相关文章
- 记一次 .NET 某智能交通后台服务 CPU爆高分析
一:背景 1. 讲故事 前天有位朋友加微信求助他的程序出现了CPU爆高的问题,开局就是一个红包,把我吓懵了! 由于是南方小年,我在老家张罗处理起来不方便,没有第一时间帮他处理,朋友在第二天上午已经找出 ...
- 记一次 .NET 某娱乐聊天流平台 CPU 爆高分析
一:背景 1.讲故事 前段时间有位朋友加微信,说他的程序直接 CPU=100%,每次只能手工介入重启,让我帮忙看下到底怎么回事,哈哈,这种CPU打满的事故,程序员压力会非常大, 我让朋友在 CPU 高 ...
- 记一次 .NET游戏站程序的 CPU 爆高分析
一:背景 1. 讲故事 上个月有个老朋友找到我,说他的站点晚高峰 CPU 会突然爆高,发了两份 dump 文件过来,如下图: 又是经典的 CPU 爆高问题,到目前为止,对这种我还是有一些经验可循的. ...
- 记一次 .NET 车联网云端服务 CPU爆高分析
一:背景 1. 讲故事 前几天有位朋友wx求助,它的程序CPU经常飙满,没找到原因,希望帮忙看一下. 这些天连续接到几个cpu爆高的dump,都看烦了,希望后面再来几个其他方面的dump,从沟通上看, ...
- 记一次 .NET 某智慧物流 WCS系统 CPU 爆高分析
一:背景 1. 讲故事 哈哈,再次见到物流类软件,上个月有位朋友找到我,说他的程序出现了 CPU 爆高,让我帮忙看下什么原因,由于那段时间在苦心研究 C++,分析和经验分享也就懈怠了,今天就给大家安排 ...
- 记一次 .NET 某电商交易平台Web站 CPU爆高分析
一:背景 1. 讲故事 已经连续写了几篇关于内存暴涨的真实案例,有点麻木了,这篇换个口味,分享一个 CPU爆高 的案例,前段时间有位朋友在 wx 上找到我,说他的一个老项目经常收到 CPU > ...
- 【API管理 APIM】APIM中对后端API服务的DNS域名缓存问题
问题描述 在使用API Management来进行API管理时,当我们后端的API DNS IP地址发生改变或者是API的域名发生改变后,通过APIM请求访问的还是是旧的域名或者IP地址,这是因API ...
- 记一次 .NET医疗布草API程序 内存暴涨分析
一:背景 1. 讲故事 我在年前写过一篇关于CPU爆高的分析文章 再记一次 应用服务器 CPU 暴高事故分析 ,当时是给同济做项目升级,看过那篇文章的朋友应该知道,最后的结论是运维人员错误的将 IIS ...
- Winform中使用HttpClient与后端api服务进行交互
前端js可以使用ajax.axios发出http请求 在c#中winform.控制台等可以通过WebRequest.WebClient.HttpClient 有关三个类的性能对比大家可以自己搜一下,这 ...
- 记一次 .NET 某游戏网站 CPU爆高分析
一:背景 1. 讲故事 这段时间经常有朋友微信上问我这个真实案例分析连载怎么不往下续了,关注我的朋友应该知道,我近二个月在研究 SQLSERVER,也写了十多篇文章,为什么要研究这东西呢? 是因为在 ...
随机推荐
- [MyArch]我的Archlinux与bspwm的重生之途
0x00 前言碎语 2023.8.19 好久不见.这些日子一直在和bspwm和archlinux打交道.自从上次NepCTF的前几天和CuB3y0nd小师傅的bspwm配置打交道之后我一发不可收拾.中 ...
- AT_abc270_g [ABC270G] Sequence in mod P 题解
题目传送门 前置知识 大步小步算法 解法 递推式为 \(x_{n}=(ax_{n-1}+b) \bmod p\),发现可以统一消去 \(\bmod p\) ,只在最后参与计算.以下过程省去模运算. 当 ...
- UVA12024 Hats 题解
题目传送门 前置知识 错位排列 题意 有 \(t\) 组询问,每次询问给定一个 \(n\),表示有 \(n\) 个人,每人各有一个属于自己的帽子,求所有人都带错帽子的概率(不要求约分至最简形式). 解 ...
- OGP协议的使用
OGP协议是一套Metatags的规格,用来标注页面,告诉我们你的网页快照.帮助社交app高效并准确的获取网页中的核心链接.标题.主图.正文摘要等信息,使得该网页在社交分享中有更好的展现体验. 如果网 ...
- 【OpenGL ES】立方体手动旋转
1 前言 本文主要介绍使用 OpenGL ES 绘制立方体,并实现手动触摸事件控制立方体旋转. 为方便控制触摸旋转,假设旋转轴始终在 xoy 平面上,设 z 轴的方向向量 u = (0, 0, ...
- maven源修改
<?xml version="1.0" encoding="UTF-8"?> <settings xmlns="http://mav ...
- IDEA从o开始的一系列操作及修改配置-快捷键汇总
IDEA从o开始的一系列操作及修改配置-快捷键汇总 下载IDEA 启动idea 安装svn插件 功能快捷键 先设置提示快捷键(纯属个人喜好) 入门快捷键 查找 编辑 小功能 自动代码提示 自动导包 T ...
- win32 - 使用CreateRemoteThread调用dll上的函数(建立管道)
Dll: // dllmain.cpp : Defines the entry point for the DLL application. #include "pch.h" #i ...
- golang常用库:gorilla/mux-http路由库使用
golang常用库:gorilla/mux-http路由库使用 golang常用库:配置文件解析库/管理工具-viper使用 golang常用库:操作数据库的orm框架-gorm基本使用 一:gola ...
- 【WiFi开发全攻略】WIFI基础知识大全
[WiFi开发全攻略]WIFI基础知识大全 1. Wi-Fi起源 现在我们大家对Wi-Fi肯定都不陌生,无论是笔记本,手机,智能电视,都离不开Wi-Fi.目前我们一般用的都是Wi-Fi5,Wi-Fi6 ...