powershellでGreen Hillsコンパイラのログ解析してみた


## コンパイルログ解析

Green Hills のコンパイラが出力するログを解析し，どのようなエラー／ワーニングが何件発生ているか解析したいと思い立ったので書いてみた。

下記は warning の例です。
:" から次の "までが問題があるソースファイル名を示しています。
また，"warning #"のあとの数値がワーニングの種類を示しています。

```dos
> complielog.log:3139:"test.c", line 78: warning #550-D: variable "testram1" was
  complielog.log:3140:          set but never used
```

これらのワードで各種情報を抽出します。

## 基本的な処理の流れ

### Log 読み込む

ログを読み込む際に "error #"や "warning #" にマッチする行を選択的に読み込む。

```powershell
$logFile = "complielog.log"
$resE = Select-String -Path $logFile -Pattern "error #" -Context  0, 3 -Encoding default
$resW = Select-String -Path $logFile -Pattern "warning #" -Context  0, 3 -Encoding default
```

### ファイル名を抽出する

エラーログ上の `:"` から `",`までの中身がファイル名となる。

```dos
> complielog.log:3139:"test.c", line 78: warning #550-D: variable "fl_testram1" was
  complielog.log:3140:          set but never used
```

```powershell
$resEfile = [RegEx]::Matches( $resE, '(?<=(:")).*?(?=(",))')
$resWfile = [RegEx]::Matches( $resW, '(?<=(:")).*?(?=(",))')
```

### エラー No を抽出する

エラーログ上の `#` から`:`でまでの中身がエラーＮｏ

```powershell
$resENo = [RegEx]::Matches( $resE, "(?<=#).*?(?=:)")
$resWNo = [RegEx]::Matches( $resW, "(?<=#).*?(?=:)")
```

### エラー No 毎の件数を計算する

もう少し賢いやり方がありそうだが，連想配列を使ってごりごり処理してみた。

```powershell
$resEhash = @{}  # 空の連想配列を作成
if ($resENo.Count -gt 0) {
    $resENo.Value | ForEach-Object {
        $val = $_
        if (! $resEhash.ContainsKey($val)) {
            $resEhash.add($val, 1) # if keyがない場合は追加する
        } else {
            $resEhash[$val] = $resEhash[$val] + 1 # else keyがある場合はインクリメントする
        }
    }
}

$resWhash = @{}  # 空の連想配列を作成
if ($resWno.Count -gt 0) {
    $resWNo.Value | ForEach-Object {
        $val = $_
        if(! $resWhash.ContainsKey($val)){
            $resWhash.add($val, 1) # if keyがない場合は追加する
        } else {
            $resWhash[$val] = $resWhash[$val] + 1 # else keyがある場合はインクリメントする
        }
    }
}
```

### エクセルに書き込む

詳細は下記 Script を参照ください。

## Script

```powershell
# compile log の を整理する。

# [使い方]
# PS > .\compilelogGrep2.ps1 -logfile .\aaa.log -outputfile aaaa.xlsx

param (
    # Param1
    [Parameter(Position = 0)]
    [String]
    $logFile = ".\GHS_compile.log",
    # Param2
    [Parameter(Position = 1)]
    [String]
    $outputfile = "compilelog_summary.xlsx"
)

# === Log読込・解析処理  ===========================================================
Write-host "error を抽出"
$resE = Select-String -Path $logFile -Pattern "error #" -Context  0, 3 -Encoding default
if ($debugMode) {
    $outputEFname = "compilelog_summary_error.csv"
    if (test-path -Path $outputEFname) { remove-item $outputEFname }
    $resE | out-file $outputEFname -Append -Encoding default -width 1000
}
write-host "error件数" $resE.count
if ($resE.Count -gt 0) {
    # ファイル名抽出
    $resEfile = [RegEx]::Matches( $resE, '(?<=(:")).*?(?=(",))')
    # エラーNo抽出
    $resENo = [RegEx]::Matches( $resE, "(?<=#).*?(?=:)")
    $resEhash = @{}  # 空の連想配列を作成
    if ($resENo.Count -gt 0) {
        $resENo.Value | ForEach-Object {
            $val = $_
            if (! $resEhash.ContainsKey($val)) {
                $resEhash.add($val, 1) # if keyがない場合は追加する
            } else {
                $resEhash[$val] = $resEhash[$val] + 1 # else keyがある場合はインクリメントする
            }
        }

    }
}

write-host "warning を抽出"
$resW = Select-String -Path $logFile -Pattern "warning #" -Context  0, 3 -Encoding default
if ($debugMode) {
    $outputWFname = "compilelog_summary_warning.csv"
    if (test-path -Path $outputWFname) { remove-item $outputWFname }
    $resW | out-file $outputWFname -Append -Encoding default -width 1000
}
write-host "warning件数" $resW.count
$resWfile = @()
$resWNo = @()
if ($resW.Count -gt 0){
    # ファイル名抽出
    $resWfile = [RegEx]::Matches( $resW, '(?<=(:")).*?(?=(",))')
    # ワーニングNo抽出
    $resWNo = [RegEx]::Matches( $resW, "(?<=#).*?(?=:)")
    $resWhash = @{}  # 空の連想配列を作成
    if ($resWno.Count -gt 0) {
        $resWNo.Value | ForEach-Object {
            $val = $_
            if (! $resWhash.ContainsKey($val)) {
                $resWhash.add($val, 1)  # if keyがない場合は追加する
            } else {
                $resWhash[$val] = $resWhash[$val] + 1 # else keyがある場合はインクリメントする
            }
        }
    }
}


# === 出力処理 ==================================================================
# Excelに出力する
$excel = New-Object -ComObject Excel.Application
$excel.Visible = $true          # 画面上に表示させない
$excel.DisplayAlerts = $true    # 警告メッセージは表示する

# # 対象の Excel ファイル名
$currentPath = (Convert-Path .)
$outputfile = join-path $currentPath $outputfile
if (Test-Path $outputfile) { Remove-Item $outputfile }      # 存在する場合は事前に削除しておく

$book = $excel.Workbooks.Add()
$SheetE = $book.Worksheets.Item(1)
$SheetE.Name = "error"
$SheetW = $book.Worksheets.Add([System.Reflection.Missing]::Value, $SheetE)
$SheetW.Name = "warning"
$SheetS = $book.Worksheets.Add($SheetE)
$SheetS.Name = "Summary"

# ---------- summary sheet -----------------
$SheetS.Columns("A").ColumnWidth = 5
$SheetS.Columns("B:C").ColumnWidth = 15

$SheetS.Range("A1") = "コンパイル結果"
$SheetS.Range("A1").Font.Bold = $true
$SheetS.Range("A1").interior.ColorIndex = 6

$SheetS.Range("B4") = "mk32 log"
$SheetS.Range("C4") = $logFile
$SheetS.Range("B5") = "Log Date"
$SheetS.Range("C5") = $(Get-ItemProperty $logFile).LastWriteTime.ToString('yyyy/MM/dd')
$SheetS.Range("B6") = "Log Time"
$SheetS.Range("C6") = $(Get-ItemProperty $logFile).LastWriteTime.ToString('HH:mm:ss')

$lop = 9
$strRange = ("C" + $lop)
$SheetS.Range($strRange) = "Error"
$SheetS.Range($strRange).Font.Bold = $true
$SheetS.Range($strRange).interior.ColorIndex = 6
$lop++
$SheetS.Range("B" + $lop) = "件数"
$SheetS.Range("C" + $lop) = $resE.count
if ($resE.Count -gt 0) {
    $lop = $lop + 2
    $SheetS.Range("B" + $lop) = "内訳"
    $SheetS.Range("C" + $lop) = "Error No"
    $SheetS.Range("D" + $lop) = "Count"
    $lop++
    foreach ($key in $resEhash.Keys) {
        $SheetS.Range("C" + $lop) = $key
        $SheetS.Range("D" + $lop) = $resEhash[$key]
        $lop++
    }
}
$lop = $lop + 3

$strRange = ("C" + $lop)
$SheetS.Range($strRange) = "Warning"
$SheetS.Range($strRange).Font.Bold = $true
$SheetS.Range($strRange).interior.ColorIndex = 6
$lop++
$SheetS.Range("B" + $lop) = "件数"
$SheetS.Range("C" + $lop) = $resW.count
if ($resW.Count -gt 0) {
    $lop = $lop + 2
    $SheetS.Range("B" + $lop) = "内訳"
    $SheetS.Range("C" + $lop) = "Warning No"
    $SheetS.Range("D" + $lop) = "Count"
    $lop++
    foreach ($key in $resWhash.Keys) {
        $SheetS.Range("C" + $lop) = $key
        $SheetS.Range("D" + $lop) = $resWhash[$key]
        $lop++
    }
} else {
$lop++
}
$SheetS.Range("A" + $lop) = "[EOL]"




# ---------- error sheet -----------------
$SheetE.Range("A1") = "処置"
$SheetE.Columns("A").ColumnWidth = 25
$SheetE.Range("B1") = "担当"
$SheetE.Range("C1") = "処置結果"
$SheetE.Columns("C").ColumnWidth = 25
$SheetE.Range("D1") = "No"
$SheetE.Columns("D").ColumnWidth = 15
$SheetE.Range("E1") = "filename"
$SheetE.Columns("E").ColumnWidth = 25
$SheetE.Range("F1") = "ログ"
$SheetE.Columns("F").ColumnWidth = 150
$SheetE.Range("A1:F1").Font.Bold = $true
$SheetE.Range("A1:F1").interior.ColorIndex = 6

if ($resE.Count -gt 0) {
    $lop = 2
    $resE | ForEach-Object {
        $strRange = ("F" + $lop)
        $SheetE.Range($strRange) = $_.ToString()
        $lop++
    }
    $lop = 2
    $resEfile.Value | ForEach-Object {
        $strRange = ("E" + $lop)
        $SheetE.Range($strRange) = $_
        $lop++
    }

    $lop = 2
    $resENo.Value | ForEach-Object {
        $strRange = ("D" + $lop)
        $SheetE.Range($strRange) = $_
        $lop++
    }
    $SheetE.Range("A" + $lop+":F"+ $lop) = "[EOL]"
    # オートフィルタを設定する
    $SheetE.Range("A:F").autofilter(1) | out-null
}


# ---------- warning sheet -----------------
$SheetW.Range("A1") = "処置"
$SheetW.Columns("A").ColumnWidth = 25
$SheetW.Range("B1") = "担当"
$SheetW.Range("C1") = "処置結果"
$SheetW.Columns("C").ColumnWidth = 25
$SheetW.Range("D1") = "No"
$SheetW.Columns("D").ColumnWidth = 15
$SheetW.Range("E1") = "Filename"
$SheetW.Columns("E").ColumnWidth = 25
$SheetW.Range("F1") = "ログ"
$SheetW.Columns("F").ColumnWidth = 150
$SheetW.Range("A1:F1").Font.Bold = $true
$SheetW.Range("A1:F1").interior.ColorIndex = 6

if ($resW.Count -gt 0) {
    $lop = 2
    $resW | ForEach-Object {
        $strRange = ("F" + $lop)
        $SheetW.Range($strRange) = $_.ToString()
        $lop++
    }
    $lop = 2
    $resWfile.Value | ForEach-Object {
        $strRange = ("E" + $lop)
        $SheetW.Range($strRange) = $_
        $lop++
    }
    $lop = 2
    $resWNo.Value | ForEach-Object {
        $strRange = ("D" + $lop)
        $SheetW.Range($strRange) = $_
        $lop++
    }
    $SheetW.Range("A" + $lop+":F"+ $lop) = "[EOL]"
    # オートフィルタを設定する
    $SheetW.Range("A:F").autofilter(1) | out-null
}

# 上書き保存
$book.SaveAs($outputfile)
# ブックを閉じる
$excel.Workbooks.Close()
$excel.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($book) | out-null
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel) | out-null

```