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
+3
View File
@@ -0,0 +1,3 @@
analyzer/*.md
analyzer/*.docx
analyzer/*.html
+149 -6
View File
@@ -65,6 +65,13 @@ type ProcessHung struct {
PID int `json:"PID"` 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 { type kv struct {
Key string Key string
Value int Value int
@@ -178,6 +185,8 @@ func main() {
devBuildCount := 0 devBuildCount := 0
secInterferenceCount := 0 secInterferenceCount := 0
memSwapCount := 0
var sumDpcInterfered float64
sysKernelHogs := make(map[string]int) sysKernelHogs := make(map[string]int)
sysNetworkHogs := make(map[string]int) sysNetworkHogs := make(map[string]int)
@@ -185,6 +194,12 @@ func main() {
sysTargetIps := make(map[string]int) sysTargetIps := make(map[string]int)
sysTargetIpsMaxIO := make(map[string]float64) sysTargetIpsMaxIO := make(map[string]float64)
hungCounts := make(map[string]int) hungCounts := make(map[string]int)
devToolsStats := make(map[string]*DevToolUsage)
// 심화 분석 지표
hourlyStress := make(map[int]int)
networkHangCorrels := 0
totalEventsParsed := 0
var details []DetailRecord var details []DetailRecord
@@ -214,26 +229,75 @@ func main() {
// 🚨 [Smoking Gun 타겟팅] // 🚨 [Smoking Gun 타겟팅]
isCompiling := false isCompiling := false
var activeDevTools []string
for _, p := range ioProcs { for _, p := range ioProcs {
if isDevTool(p.ProcessName) && p.IODataBytesPersec > 1048576 { 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 isCompiling = true
break
} }
} }
if isCompiling { if isCompiling {
devBuildCount++ devBuildCount++
secInterfered := false secInterfered := false
var secCulprits []string
if r.DpcInterrupt > 3.0 { secInterfered = true } if r.DpcInterrupt > 3.0 { secInterfered = true }
for _, cp := range cpuProcs { 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. 커널 프리징 // 1. 커널 프리징
if r.DpcInterrupt > 3.0 { if r.DpcInterrupt > 3.0 {
isEventOccurred = true
rec := DetailRecord{Category: fmt.Sprintf("[프리징 유발] 커널 오버헤드 감지 - 시간: %s | PC: %s | 오버헤드: %.1f%%", localTimeStr, r.Hostname, r.DpcInterrupt)} rec := DetailRecord{Category: fmt.Sprintf("[프리징 유발] 커널 오버헤드 감지 - 시간: %s | PC: %s | 오버헤드: %.1f%%", localTimeStr, r.Hostname, r.DpcInterrupt)}
for _, cp := range cpuProcs { for _, cp := range cpuProcs {
if cp.KernelCPUPercent > 3.0 && !isIgnoredProcess(cp.ProcessName) { if cp.KernelCPUPercent > 3.0 && !isIgnoredProcess(cp.ProcessName) {
@@ -255,6 +319,7 @@ func main() {
if !isNetworkChoke { if !isNetworkChoke {
isNetworkChoke = true isNetworkChoke = true
isEventOccurred = true
netRec = DetailRecord{Category: fmt.Sprintf("[대역폭 포화 감지] 네트워크/디스크 지연 - 시간: %s | PC: %s", localTimeStr, r.Hostname)} netRec = DetailRecord{Category: fmt.Sprintf("[대역폭 포화 감지] 네트워크/디스크 지연 - 시간: %s | PC: %s", localTimeStr, r.Hostname)}
} }
@@ -280,9 +345,9 @@ func main() {
// 3. 앱 Hang // 3. 앱 Hang
var hung []ProcessHung var hung []ProcessHung
json.Unmarshal([]byte(r.HungProcesses), &hung) json.Unmarshal([]byte(r.HungProcesses), &hung)
hasValidHang := false
if len(hung) > 0 { if len(hung) > 0 {
hangRec := DetailRecord{Category: fmt.Sprintf("[응답 없음] 프로세스 Hang 발생 - 시간: %s | PC: %s", localTimeStr, r.Hostname)} hangRec := DetailRecord{Category: fmt.Sprintf("[응답 없음] 프로세스 Hang 발생 - 시간: %s | PC: %s", localTimeStr, r.Hostname)}
hasValidHang := false
for _, h := range hung { for _, h := range hung {
if !isIgnoredProcess(h.ProcessName) { if !isIgnoredProcess(h.ProcessName) {
hungCounts[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)) 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) err = ioutil.WriteFile(reportMd, []byte(md.String()), 0644)
if err != nil { log.Fatalf("MD 파일 저장 실패: %v", err) } 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("==================================================\n")
fmt.Printf(" 분석 완료: '%s' '%s' 2종류의 문서가 생성되었습니다.\n", reportDocx, reportMd) fmt.Printf(" 분석 완료: '%s', '%s', '%s' 문서가 생성되었습니다.\n", reportDocx, reportMd, reportHtml)
fmt.Printf("==================================================\n") fmt.Printf("==================================================\n")
} }
+378
View File
@@ -0,0 +1,378 @@
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
)
type ReportData struct {
Timestamp string `json:"timestamp"`
DevBuildCount int `json:"devBuildCount"`
SecInterferenceCount int `json:"secInterferenceCount"`
InterferenceRate float64 `json:"interferenceRate"`
KernelHogs []kv `json:"kernelHogs"`
NetworkHogs []kv `json:"networkHogs"`
TargetIps []kv `json:"targetIps"`
HungCounts []kv `json:"hungCounts"`
Details []DetailRecord `json:"details"`
EtlDrivers []kv `json:"etlDrivers"`
MemSwapEvents int `json:"memSwapEvents"`
AvgDpcWhenInterfered float64 `json:"avgDpcWhenInterfered"`
DevToolsStats []DevToolUsage `json:"devToolsStats"`
VDIHealthScore float64 `json:"vdiHealthScore"`
HourlyStress []kv `json:"hourlyStress"`
NetworkHangCorrels int `json:"networkHangCorrels"`
}
func generateHTMLReport(filename string, data ReportData) error {
jsonData, err := json.Marshal(data)
if err != nil {
return err
}
htmlTemplate := `<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>시스템 성능 분석 논문</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.tailwindcss.com?plugins=typography"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
body { background-color: #fcfcfc; }
.academic-font { font-family: 'Nanum Myeongjo', 'Batang', 'Georgia', serif; }
.sans-font { font-family: 'Malgun Gothic', 'Helvetica Neue', Arial, sans-serif; }
figure { border-bottom: 1px solid #e5e7eb; padding-bottom: 1rem; margin-bottom: 2rem; }
figcaption { font-size: 0.875rem; color: #4b5563; margin-top: 0.5rem; text-align: justify; word-break: keep-all; }
.nature-header { border-bottom: 4px solid #222; padding-bottom: 1rem; margin-bottom: 2rem; }
h1, h2, h3, h4, h5, h6 { font-family: 'Nanum Myeongjo', 'Batang', serif; word-break: keep-all; }
p { word-break: keep-all; }
</style>
</head>
<body class="academic-font text-gray-900 leading-relaxed p-4 md:p-12">
<article class="max-w-4xl mx-auto bg-white p-8 md:p-16 shadow-lg border border-gray-200">
<!-- Header Section -->
<header class="nature-header">
<div class="text-sm font-bold text-red-700 tracking-wider mb-2 sans-font uppercase">Research Article</div>
<h1 class="text-3xl md:text-4xl font-bold mb-4 leading-tight">엔터프라이즈 데스크톱 환경 내 보안 솔루션과 개발 워크로드 간의 자원 경합 및 시스템 지연(Lag)에 대한 실증적 분석</h1>
<div class="flex flex-col md:flex-row justify-between items-baseline mb-4 text-sm sans-font">
<div>
<span class="font-bold text-gray-800">시스템 성능 분석 연구소 (System Performance Analysis Lab)</span><br>
<span class="text-gray-600">자동화 분석 사업부</span>
</div>
<div class="text-gray-500 mt-2 md:mt-0" id="reportTime"></div>
</div>
</header>
<!-- Abstract -->
<section class="mb-10 text-justify">
<h2 class="text-xl font-bold mb-3">초록 (Abstract)</h2>
<p class="font-semibold text-gray-800 mb-4">
엔터프라이즈 데스크톱 환경에서 발생하는 시스템 성능 저하는 대량의 I/O를 발생시키는 개발 워크로드와 이를 실시간으로 감시하는 보안 솔루션 간의 자원 경합에서 기인하는 경우가 많다. 본 연구는 백엔드 텔레메트리 데이터를 정량적으로 분석하여 만성적인 시스템 지연의 근본 원인을 규명하고, 새롭게 고안된 시스템 건강도 지수(Health Score) 및 시간대별 스트레스 패턴을 통해 해결 방안을 모색한다.
</p>
<p id="abstractDynamic"></p>
</section>
<!-- Main Content -->
<div class="prose prose-lg max-w-none text-justify">
<h3>1. 서론 및 연구 방법</h3>
<p>
시스템 메트릭을 지속적으로 모니터링한 결과, 과도한 I/O 작업이 커널 레벨의 병목을 유발하는 패턴이 확인되었다. 본 연구는 CPU, 메모리, DPC(지연된 프로시저 호출) 인터럽트, 프로세스별 I/O 포화도 등 시계열 데이터를 분석하였다. 특히 보안 에이전트가 소스 코드 컴파일이나 컨테이너 빌드와 같은 대규모 I/O 이벤트에 과도하게 개입(Hooking)하여 DPC 인터럽트를 증가시키고 시스템 프리징을 유발한다는 가설을 검증하고자 한다.
</p>
<h3>2. 분석 결과: 자원 경합 및 병목 현상</h3>
<p>
수집된 실증 데이터는 성능 저하를 유발하는 구체적인 원인 프로세스들을 지목하고 있다. 이를 크게 커널 병목(DPC/EDR), 네트워크 및 디스크 I/O 포화, 애플리케이션 응답 없음(Hang)의 세 가지 벡터로 분류하여 분석하였다.
</p>
<!-- Figures Grid -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-8 my-8 not-prose">
<figure>
<div class="bg-gray-50 border border-gray-200 p-4">
<canvas id="kernelChart"></canvas>
</div>
<figcaption><strong>Figure 1. 커널 레벨 오버헤드.</strong> 치명적인 DPC 인터럽트 스파이크(>3%%)를 유발한 프로세스의 빈도로, 주로 EDR 및 백신의 실시간 감시 지연을 나타낸다.</figcaption>
</figure>
<figure>
<div class="bg-gray-50 border border-gray-200 p-4">
<canvas id="networkChart"></canvas>
</div>
<figcaption><strong>Figure 2. I/O 및 대역폭 포화.</strong> 심각한 대역폭 고갈을 유발하여 디스크 큐(Queue) 대기열을 증가시키고 스레드 기아 상태를 초래한 프로세스 분포.</figcaption>
</figure>
</div>
<p id="resultsDynamic"></p>
<h3>3. 심화 분석: 보안 솔루션의 영향도 및 상관관계</h3>
<div class="bg-gray-100 p-6 border-l-4 border-gray-800 my-6 not-prose sans-font">
<h4 class="font-bold text-gray-900 mb-4 text-lg">핵심 통계 지표 (Statistical Highlights)</h4>
<ul class="grid grid-cols-2 gap-4 text-sm mb-6">
<li><span class="block text-gray-500">시스템 건강도 점수 (Health Score)</span><strong class="text-xl text-green-600" id="vdiHealth">100 / 100</strong></li>
<li><span class="block text-gray-500">네트워크 포화 - 응답없음 연관 사례</span><strong class="text-xl" id="networkHangCorrels">0</strong></li>
<li><span class="block text-gray-500">개발 도구 빌드 감지</span><strong class="text-xl" id="devBuildCount">0</strong></li>
<li><span class="block text-gray-500">보안 솔루션 개입 (Interference)</span><strong class="text-xl text-red-600" id="secInterferenceCount">0</strong></li>
<li><span class="block text-gray-500">업무 방해 확률 (Interference Rate)</span><strong class="text-xl" id="interferenceRate">0%%</strong></li>
<li><span class="block text-gray-500">개입 시 평균 DPC 오버헤드</span><strong class="text-xl" id="avgDpc">0%%</strong></li>
</ul>
<h4 class="font-bold text-gray-900 mb-2 mt-4 text-md">Table 1. 감지된 개발 워크로드 및 최대 자원 사용량</h4>
<div class="overflow-x-auto shadow-sm border border-gray-200 bg-white">
<table class="w-full text-sm text-left">
<thead class="bg-gray-50 text-gray-700">
<tr>
<th class="p-2 border-b">프로세스 명</th>
<th class="p-2 border-b text-center">감지 횟수</th>
<th class="p-2 border-b text-right">최대 CPU (%%)</th>
<th class="p-2 border-b text-right">최대 I/O (MB/s)</th>
</tr>
</thead>
<tbody id="devToolsTable" class="divide-y divide-gray-100">
<!-- JS Injection -->
</tbody>
</table>
</div>
</div>
<p>
Table 1에서 관찰할 수 있듯이, 개발 워크로드는 본질적으로 높은 I/O 처리량을 요구한다. 개발 환경의 잦은 파일 생성 및 수정은 보안 에이전트의 파일 핸들 인터셉트로 이어지며, 이는 연쇄적인 커널 CPU 점유율 상승과 DPC 지연 시간 증가를 낳는다. 이러한 메커니즘이 사용자가 체감하는 "프리징(Freezing)"의 주요 매개체로 작용한다.
</p>
<figure class="my-8 not-prose">
<div class="bg-gray-50 border border-gray-200 p-4 max-w-2xl mx-auto">
<canvas id="hourlyChart"></canvas>
</div>
<figcaption><strong>Figure 3. 시간대별 시스템 스트레스 패턴.</strong> 하루 중 시스템 이벤트(프리징, 포화, 응답없음)가 집중적으로 발생한 시간대를 나타내며, 출근 직후의 부트 스톰이나 일과 중 빌드 집중 시간을 유추할 수 있다.</figcaption>
</figure>
<figure class="my-8 not-prose">
<div class="bg-gray-50 border border-gray-200 p-4 max-w-2xl mx-auto">
<canvas id="etlChart"></canvas>
</div>
<figcaption><strong>Figure 4. ETW 커널 드라이버 분석.</strong> Windows 커널 추적(.etl)을 심층 분석하여 가장 많은 인터럽트 및 파일 시스템 훅을 발생시킨 서드파티 드라이버(<code>.sys</code>)를 식별한 결과이다.</figcaption>
</figure>
<h3>4. 결론 및 제언</h3>
<p>
본 분석 결과, 과도한 I/O가 동반되는 개발 워크로드와 보안 솔루션 간의 충돌이 시스템 성능 저하의 핵심 원인임이 입증되었다. 이러한 문제를 완화하기 위해 알려진 개발 디렉토리 및 빌드 도구에 대해 보안 소프트웨어 내 예외 처리(Whitelisting)를 적용할 것을 강력히 권고한다. 또한 대량의 I/O 작업 중 발생하는 휴리스틱 스캐닝 파라미터를 튜닝함으로써 관측된 DPC 지연 스파이크를 예방하고 시스템 안정성을 회복할 수 있을 것이다.
</p>
<hr class="my-10 border-gray-300">
<!-- Appendix: Logs -->
<h3>부록 (Appendix): 시간대별 상세 이상 징후 발생 이력</h3>
<p class="text-sm">특정 성능 저하 이벤트에 대한 시계열 텔레메트리 상세 기록이다.</p>
<div class="not-prose overflow-x-auto my-6 text-sm sans-font shadow-sm border border-gray-200">
<table class="w-full text-left border-collapse">
<thead class="bg-gray-100 text-gray-700">
<tr>
<th class="p-3 border-b">#</th>
<th class="p-3 border-b">분류 / 이벤트 유형</th>
<th class="p-3 border-b">상세 원인 및 지표</th>
</tr>
</thead>
<tbody id="detailsTableBody" class="text-gray-600 divide-y divide-gray-100 bg-white">
<!-- JS Injection -->
</tbody>
</table>
</div>
</div>
</article>
<script>
const rawData = %s;
// Populate DOM elements
document.getElementById('reportTime').textContent = "게재 일자: " + rawData.timestamp;
const vdiScoreElement = document.getElementById('vdiHealth');
vdiScoreElement.textContent = rawData.vdiHealthScore.toFixed(1) + " / 100";
if(rawData.vdiHealthScore < 70) {
vdiScoreElement.className = "text-xl text-red-600 font-bold";
} else if(rawData.vdiHealthScore < 90) {
vdiScoreElement.className = "text-xl text-yellow-600 font-bold";
}
document.getElementById('networkHangCorrels').textContent = rawData.networkHangCorrels + " 건";
// Dynamic Text Generation
const abstractP = document.getElementById('abstractDynamic');
if (rawData.devBuildCount > 0) {
let text = "분석 결과, 총 <strong>" + rawData.devBuildCount + "</strong>회의 유의미한 빌드 및 컴파일 작업이 감지되었다. ";
if (rawData.interferenceRate > 0) {
text += "특히 이 중 <strong>" + rawData.interferenceRate.toFixed(1) + "%%</strong>의 사례에서 보안 솔루션의 공격적인 개입이 확인되었으며, " +
"이로 인해 커널 DPC 오버헤드가 평균 <strong>" + rawData.avgDpcWhenInterfered.toFixed(2) + "%%</strong>까지 치솟아 급성 시스템 프리징과 직접적인 상관관계를 보였다. ";
} else {
text += "다행히 본 측정 기간 동안 보안 솔루션이 빌드 작업에 과도하게 개입하지 않아, 기준 커널 안정성이 유지된 것으로 확인되었다. ";
}
if (rawData.memSwapEvents > 0) {
text += "더불어, 총 <strong>" + rawData.memSwapEvents + "</strong>회의 심각한 메모리 스와핑(Memory Swapping) 현상이 관측되어 I/O 지연을 한층 악화시킨 것으로 나타났다. ";
}
abstractP.innerHTML = text;
} else {
abstractP.innerHTML = "모니터링 기간 동안 유의미한 개발 빌드 워크로드는 감지되지 않았다. 따라서 관측된 시스템 성능 저하는 컴파일 자원 경합보다는 백그라운드 서비스 또는 일반적인 사용자 애플리케이션의 오동작에서 기인했을 가능성이 높다.";
}
const resultsP = document.getElementById('resultsDynamic');
let resText = "수집된 지표를 종합한 결과, ";
if (rawData.kernelHogs && rawData.kernelHogs.length > 0) {
resText += "커널 병목을 유발한 가장 치명적인 프로세스는 <strong>" + rawData.kernelHogs[0].Key + "</strong>(으)로 확인되었다. ";
} else {
resText += "커널 레벨에서 심각한 DPC 병목 현상은 발견되지 않았다. ";
}
if (rawData.networkHogs && rawData.networkHogs.length > 0) {
resText += "동시에 I/O 포화 상태를 주도한 주범은 <strong>" + rawData.networkHogs[0].Key + "</strong>(이)었다. ";
} else {
resText += "I/O 포화도 역시 허용 가능한 임계치 내에 머물렀다. ";
}
const totalHangs = rawData.hungCounts ? rawData.hungCounts.reduce((a, b) => a + b.Value, 0) : 0;
if (totalHangs > 0) {
resText += "이러한 자원 경합이 발생하는 동안 총 <strong>" + totalHangs + "</strong>회의 애플리케이션 응답 없음(Hang) 현상이 기록되어 사용자 경험이 크게 저하되었음을 알 수 있다. ";
if (rawData.networkHangCorrels > 0) {
resText += "특히 대역폭 포화와 응답 없음 현상이 동반 발생한 사례가 <strong>" + rawData.networkHangCorrels + "</strong>건 발견되어 높은 상관관계를 시사한다.";
}
} else {
resText += "긍정적인 점은, 해당 기간 동안 완전한 애플리케이션 응답 없음(Hang) 현상은 보고되지 않았다는 것이다.";
}
resultsP.innerHTML = resText;
document.getElementById('devBuildCount').textContent = rawData.devBuildCount;
document.getElementById('secInterferenceCount').textContent = rawData.secInterferenceCount;
document.getElementById('interferenceRate').textContent = rawData.interferenceRate.toFixed(1) + "%%";
document.getElementById('avgDpc').textContent = rawData.avgDpcWhenInterfered.toFixed(2) + "%%";
// Chart defaults
Chart.defaults.font.family = "'Malgun Gothic', 'Helvetica Neue', 'Arial', sans-serif";
Chart.defaults.color = '#4b5563';
// Kernel Hogs Chart
const kernelHogs = (rawData.kernelHogs || []).slice(0, 5);
new Chart(document.getElementById('kernelChart').getContext('2d'), {
type: 'bar',
data: {
labels: kernelHogs.map(h => h.Key),
datasets: [{
label: '프리징 유발 횟수',
data: kernelHogs.map(h => h.Value),
backgroundColor: 'rgba(153, 27, 27, 0.7)',
borderColor: 'rgb(153, 27, 27)',
borderWidth: 1
}]
}
});
// Network Hogs Chart
const netHogs = (rawData.networkHogs || []).slice(0, 5);
new Chart(document.getElementById('networkChart').getContext('2d'), {
type: 'bar',
data: {
labels: netHogs.map(h => h.Key),
datasets: [{
label: '대역폭 포화 횟수',
data: netHogs.map(h => h.Value),
backgroundColor: 'rgba(30, 64, 175, 0.7)',
borderColor: 'rgb(30, 64, 175)',
borderWidth: 1
}]
}
});
// Hourly Stress Chart
const hourlyStress = rawData.hourlyStress || [];
new Chart(document.getElementById('hourlyChart').getContext('2d'), {
type: 'line',
data: {
labels: hourlyStress.map(h => h.Key),
datasets: [{
label: '시간대별 이상 징후 (건)',
data: hourlyStress.map(h => h.Value),
backgroundColor: 'rgba(234, 88, 12, 0.2)',
borderColor: 'rgb(234, 88, 12)',
borderWidth: 2,
fill: true,
tension: 0.3
}]
}
});
// ETL Chart
const etlDrivers = (rawData.etlDrivers || []).slice(0, 5);
new Chart(document.getElementById('etlChart').getContext('2d'), {
type: 'bar',
data: {
labels: etlDrivers.map(h => h.Key),
datasets: [{
label: '인터럽트 빈도',
data: etlDrivers.map(h => h.Value),
backgroundColor: 'rgba(17, 24, 39, 0.7)',
borderColor: 'rgb(17, 24, 39)',
borderWidth: 1
}]
},
options: { indexAxis: 'y' }
});
// Populate Dev Tools Table
const devTable = document.getElementById('devToolsTable');
const devStats = rawData.devToolsStats || [];
if (devStats.length > 0) {
devStats.sort((a,b) => b.count - a.count).forEach(dt => {
devTable.innerHTML += "<tr>" +
"<td class=\"p-2 border-b\"><code>" + dt.processName + "</code></td>" +
"<td class=\"p-2 border-b text-center\">" + dt.count + "</td>" +
"<td class=\"p-2 border-b text-right text-red-600 font-semibold\">" + dt.maxCpu.toFixed(1) + "%%</td>" +
"<td class=\"p-2 border-b text-right text-blue-600 font-semibold\">" + dt.maxIo.toFixed(1) + " MB/s</td>" +
"</tr>";
});
} else {
devTable.innerHTML = "<tr><td colspan=\"4\" class=\"p-3 text-center italic text-gray-400\">감지된 개발 워크로드가 없습니다.</td></tr>";
}
// Populate Details Table
const tbody = document.getElementById('detailsTableBody');
const detailsList = rawData.details || [];
const limit = Math.min(detailsList.length, 100);
if (limit === 0) {
tbody.innerHTML = '<tr><td colspan="3" class="p-3 text-center italic text-gray-400">발생한 이상 징후 기록이 없습니다.</td></tr>';
} else {
for(let i=0; i<limit; i++) {
const d = detailsList[i];
const tr = document.createElement('tr');
tr.className = i %% 2 === 0 ? 'bg-white' : 'bg-gray-50';
const td1 = document.createElement('td');
td1.className = 'p-3 text-xs';
td1.textContent = i + 1;
const td2 = document.createElement('td');
td2.className = 'p-3 font-semibold text-gray-800 text-sm';
td2.textContent = d.Category;
const td3 = document.createElement('td');
td3.className = 'p-3';
td3.innerHTML = '<ul class="list-disc list-inside text-xs space-y-1">' +
(d.Content || []).map(c => '<li>'+c+'</li>').join('') +
'</ul>';
tr.appendChild(td1);
tr.appendChild(td2);
tr.appendChild(td3);
tbody.appendChild(tr);
}
}
</script>
</body>
</html>`
htmlContent := fmt.Sprintf(htmlTemplate, string(jsonData))
return ioutil.WriteFile(filename, []byte(htmlContent), 0644)
}
Binary file not shown.