This commit is contained in:
rl544
2026-09-15 01:33:45 +09:00
parent 4a5a0be6c9
commit df3d94c06a
4 changed files with 530 additions and 6 deletions
+149 -6
View File
@@ -65,6 +65,13 @@ type ProcessHung struct {
PID int `json:"PID"`
}
type DevToolUsage struct {
ProcessName string `json:"processName"`
Count int `json:"count"`
MaxCPU float64 `json:"maxCpu"`
MaxIO float64 `json:"maxIo"`
}
type kv struct {
Key string
Value int
@@ -178,6 +185,8 @@ func main() {
devBuildCount := 0
secInterferenceCount := 0
memSwapCount := 0
var sumDpcInterfered float64
sysKernelHogs := make(map[string]int)
sysNetworkHogs := make(map[string]int)
@@ -185,7 +194,13 @@ func main() {
sysTargetIps := make(map[string]int)
sysTargetIpsMaxIO := make(map[string]float64)
hungCounts := make(map[string]int)
devToolsStats := make(map[string]*DevToolUsage)
// 심화 분석 지표
hourlyStress := make(map[int]int)
networkHangCorrels := 0
totalEventsParsed := 0
var details []DetailRecord
rows, err := db.Query(`
@@ -214,26 +229,75 @@ func main() {
// 🚨 [Smoking Gun 타겟팅]
isCompiling := false
var activeDevTools []string
for _, p := range ioProcs {
if isDevTool(p.ProcessName) && p.IODataBytesPersec > 1048576 {
mbps := p.IODataBytesPersec / 1048576.0
cpuVal := 0.0
for _, cp := range cpuProcs {
if cp.ProcessName == p.ProcessName && cp.PID == p.PID {
cpuVal = cp.CPUPercent; break
}
}
if stat, ok := devToolsStats[p.ProcessName]; ok {
stat.Count++
if cpuVal > stat.MaxCPU { stat.MaxCPU = cpuVal }
if mbps > stat.MaxIO { stat.MaxIO = mbps }
} else {
devToolsStats[p.ProcessName] = &DevToolUsage{ProcessName: p.ProcessName, Count: 1, MaxCPU: cpuVal, MaxIO: mbps}
}
activeDevTools = append(activeDevTools, fmt.Sprintf("%s (CPU: %.1f%%, I/O: %.1f MB/s)", p.ProcessName, cpuVal, mbps))
isCompiling = true
break
}
}
if isCompiling {
devBuildCount++
secInterfered := false
var secCulprits []string
if r.DpcInterrupt > 3.0 { secInterfered = true }
for _, cp := range cpuProcs {
if isSecProc(cp.ProcessName) && cp.KernelCPUPercent > 2.0 { secInterfered = true; break }
if isSecProc(cp.ProcessName) && cp.KernelCPUPercent > 2.0 {
secInterfered = true
secCulprits = append(secCulprits, fmt.Sprintf("%s (Kernel CPU: %.1f%%)", cp.ProcessName, cp.KernelCPUPercent))
}
}
if secInterfered { secInterferenceCount++ }
if secInterfered {
secInterferenceCount++
sumDpcInterfered += r.DpcInterrupt
rec := DetailRecord{
Category: fmt.Sprintf("[빌드-보안 충돌] 개발 도구와 보안 솔루션 경합 감지 - 시간: %s | PC: %s", localTimeStr, r.Hostname),
Content: []string{
"감지된 개발 프로세스: " + strings.Join(activeDevTools, ", "),
"관여된 보안 솔루션: " + strings.Join(secCulprits, ", "),
fmt.Sprintf("발생 커널 오버헤드(DPC): %.1f%%", r.DpcInterrupt),
},
}
details = append(details, rec)
}
}
if r.TotalMem > 90.0 {
hasHighIO := false
for _, p := range ioProcs {
if p.IODataBytesPersec > 1048576 {
hasHighIO = true; break
}
}
if hasHighIO { memSwapCount++ }
}
// 상세 내역 및 일반 요약 로직
isEventOccurred := false
// 1. 커널 프리징
if r.DpcInterrupt > 3.0 {
isEventOccurred = true
rec := DetailRecord{Category: fmt.Sprintf("[프리징 유발] 커널 오버헤드 감지 - 시간: %s | PC: %s | 오버헤드: %.1f%%", localTimeStr, r.Hostname, r.DpcInterrupt)}
for _, cp := range cpuProcs {
if cp.KernelCPUPercent > 3.0 && !isIgnoredProcess(cp.ProcessName) {
@@ -255,6 +319,7 @@ func main() {
if !isNetworkChoke {
isNetworkChoke = true
isEventOccurred = true
netRec = DetailRecord{Category: fmt.Sprintf("[대역폭 포화 감지] 네트워크/디스크 지연 - 시간: %s | PC: %s", localTimeStr, r.Hostname)}
}
@@ -280,9 +345,9 @@ func main() {
// 3. 앱 Hang
var hung []ProcessHung
json.Unmarshal([]byte(r.HungProcesses), &hung)
hasValidHang := false
if len(hung) > 0 {
hangRec := DetailRecord{Category: fmt.Sprintf("[응답 없음] 프로세스 Hang 발생 - 시간: %s | PC: %s", localTimeStr, r.Hostname)}
hasValidHang := false
for _, h := range hung {
if !isIgnoredProcess(h.ProcessName) {
hungCounts[h.ProcessName]++
@@ -290,8 +355,20 @@ func main() {
hangRec.Content = append(hangRec.Content, fmt.Sprintf("멈춤: %s%s (PID: %d)", h.ProcessName, getProcessTag(h.ProcessName), h.PID))
}
}
if hasValidHang { details = append(details, hangRec) }
if hasValidHang {
details = append(details, hangRec)
isEventOccurred = true
}
}
// 심화 분석: 상관관계 및 시간대별 스트레스 추적
if isNetworkChoke && hasValidHang {
networkHangCorrels++ // 대역폭 포화가 앱 응답없음을 유발한 연관성 의심 사례
}
if isEventOccurred {
hourlyStress[r.Timestamp.Hour()]++
}
totalEventsParsed++
}
}
@@ -402,8 +479,74 @@ func main() {
err = ioutil.WriteFile(reportMd, []byte(md.String()), 0644)
if err != nil { log.Fatalf("MD 파일 저장 실패: %v", err) }
// --- HTML Report Data Preparation ---
var rate float64 = 0
if devBuildCount > 0 {
rate = (float64(secInterferenceCount) / float64(devBuildCount)) * 100
}
// Calculate advanced metrics
var avgDpc float64 = 0
if secInterferenceCount > 0 {
avgDpc = sumDpcInterfered / float64(secInterferenceCount)
}
healthScore := 100.0
healthScore -= float64(devBuildCount) * 0.1
healthScore -= float64(secInterferenceCount) * 1.5
healthScore -= float64(memSwapCount) * 2.0
healthScore -= float64(len(hungCounts)) * 1.0
if healthScore < 0 { healthScore = 0 }
// Prepare flat slices for charts
kernelHogsSlice := sortMap(sysKernelHogs)
networkHogsSlice := sortMap(sysNetworkHogs)
targetIpsSlice := sortMap(sysTargetIps)
hungCountsSlice := sortMap(hungCounts)
var etlDriversSlice []kv
if len(etlFiles) > 0 {
sysMap := parseETL(etlFiles[0])
etlDriversSlice = sortMap(sysMap)
}
var devStatsSlice []DevToolUsage
for _, v := range devToolsStats {
devStatsSlice = append(devStatsSlice, *v)
}
var hourlyStressSlice []kv
for i := 0; i < 24; i++ {
hourlyStressSlice = append(hourlyStressSlice, kv{Key: fmt.Sprintf("%02d:00", i), Value: hourlyStress[i]})
}
reportData := ReportData{
Timestamp: time.Now().Format("2006-01-02 15:04:05"),
DevBuildCount: devBuildCount,
SecInterferenceCount: secInterferenceCount,
InterferenceRate: rate,
KernelHogs: kernelHogsSlice,
NetworkHogs: networkHogsSlice,
TargetIps: targetIpsSlice,
HungCounts: hungCountsSlice,
Details: details,
EtlDrivers: etlDriversSlice,
MemSwapEvents: memSwapCount,
AvgDpcWhenInterfered: avgDpc,
DevToolsStats: devStatsSlice,
VDIHealthScore: healthScore,
HourlyStress: hourlyStressSlice,
NetworkHangCorrels: networkHangCorrels,
}
reportHtml := fmt.Sprintf("Report_%s.html", timestampStr)
err = generateHTMLReport(reportHtml, reportData)
if err != nil {
log.Printf("HTML 파일 저장 실패: %v", err)
}
fmt.Printf("==================================================\n")
fmt.Printf(" 분석 완료: '%s' '%s' 2종류의 문서가 생성되었습니다.\n", reportDocx, reportMd)
fmt.Printf(" 분석 완료: '%s', '%s', '%s' 문서가 생성되었습니다.\n", reportDocx, reportMd, reportHtml)
fmt.Printf("==================================================\n")
}