SPECIALIST

多様な専門性を持つNRIデジタル社員のコラム、インタビューやインサイトをご紹介します。

BACK

Amazon CodeGuru Profilerはどんな動的解析が可能なのか

こんにちは、NRIデジタルの島です。

昨年6月GAとなった「Amazon CodeGuru(以下CodeGuru)」を試してみたので、その内容を共有したいと思います。

CodeGuruとは

AWS AIサービスのひとつで、機械学習ベースでコードの欠陥やアプリケーションで実行コストの高い箇所を自動的に分析し、改善方法含め奨励事項を提案してくれるサービスです。
大きく分けて下記2つの機能があり、それぞれ単独で利用可能です。

・CodeGuru Reviewer(以下Reviewer)
Git上のコードに対してプルリクエストをトリガーに、バグの特定とその改善方法を提示してくれます。

・CodeGuru Profiler(以下Profiler)
Profilerエージェントをアプリケーションに組み込むことで、メトリックの可視化と実行コストの高いコードに対して、改善方法を提示してくれます。

執筆時点で、Reviewerの対象リポジトリはGitHubやAWS CodeCommit等で、Reviewer/Profilerの対象言語はJava及びPythonとなります。
詳細は製品ページをご参照ください。
また、開発工程におけるCodeGuruの立ち位置等はAWS DevOpsブログにわかりやすくまとまっていますので、是非ご覧いただければと思います。

今回はProfilerをメインに試してみました。

Profilerを試す

一般的に動的解析でなければ検出しづらいデッドロックなどのスレッド系問題や、OutOfMemoryErrorのようなリソース系問題を検出できるのか、について検証してみました。

AWS管理コンソールよりプロファイリンググループを作成し、対象のアプリケーション(easybuggy)に対してGuruプロファイラAgentを指定し、起動します。
※設定、実行手順はプロファイリンググループを作成する際のページに記載されています。

>java javaagent:codeguru-profiler-java-agent-standalone-1.1.1.jar="profilingGroupName:easy-buggy,heapSummaryEnabled:true" -Xmx256m -jar easybuggy4sb.jar

今回利用した「easybuggy」と言うアプリケーションは、様々なバグが仕込んであるWebアプリケーションで、下記のような障害を明示的に発生させられます。

この中から、コードレビューでは特に発見しにくい「メモリリーク(Javaヒープ領域)」を実施し、改善事項を提案してくれるか、確認してみました。

該当のソースコードは下記です。

@Controller
public class MemoryLeakController extends AbstractController {
  
    private HashMap<String, String> cache = new HashMap<>();
  
   @RequestMapping(value = "/memoryleak")
   public ModelAndView process(ModelAndView mav, Locale locale) {
        setViewAndCommonObjects(mav, locale, "memoryleak");
        toDoRemove();
  
        List<MemoryPoolMXBean> heapPoolMXBeans = new ArrayList<>();
        List<MemoryPoolMXBean> memoryPoolMXBeans = ManagementFactory.getMemoryPoolMXBeans();
        for (MemoryPoolMXBean memoryPoolMXBean : memoryPoolMXBeans) {
            if (MemoryType.HEAP.equals(memoryPoolMXBean.getType())) {
                heapPoolMXBeans.add(memoryPoolMXBean);
            }
        }
        mav.addObject("memoryPoolMXBeans", heapPoolMXBeans);
      return mav;
   }
  
   private void toDoRemove() {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 100000; i++) {
            sb.append("Memory leak occurs!");
        }
        cache.put(String.valueOf(sb.hashCode()), sb.toString());
   }
}

 

ページをリロードし続けると、ヒープ使用量がどんどん上がっていきやがてOutOfMemoryErrorが発生します。

[Handler dispatch failed; nested exception is java.lang.OutOfMemoryError: Java heap space] with root cause
  
java.lang.OutOfMemoryError: Java heap space
        at java.util.Arrays.copyOf(Arrays.java:3332) ~[na:1.8.0_252]
        at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:124) ~[na:1.8.0_252]
        at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:448) ~[na:1.8.0_252]
-----

 

下記がProfilerページでの結果です。
メトリックは認識してくれてますが、残念ながらメモリリークに対する改善事項の提案はしてくれませんでした。

メモリリーク以外の他シナリオも実施してみましたが、特に提案はありませんでした。

・無限ループ
・スレッドリーク
・デッドロック
・ファイルディスクリプタリーク
etc…

どうやらProfilerが対象としているのは、上記のようなリークやバグのようなものではなく、パフォーマンス改善を中心に見ているようです。
その為、非効率なインスタンス生成に対する指摘や、並列化すると処理性能が改善するような観点で提案をしてくれるようで、今回実施したバグのような類はReviewerで検知すべきという方針とのようです。

Reviewerに本アプリケーションのコードをかけてみたところ、確かにデッドロック等のバグを指摘してくれました。

デッドロック指摘の例

なお、返信でフィードバックすることでより賢くなっていくようです。

さいごに

Profilerは今回のようなスポットでの確認でなく、アプリケーションを長期的に動かしていく中で、パフォーマンス上のボトルネックの検出と改善事項の提案を行ってくれるサービスということが理解できました。
今回はProfilerが提供する中心的な機能ではない領域の検証ということで期待する結果が得られませんでしたが、性能試験を実施している環境などで試してみると効果的な改善提案をしてくれるのではないかと思います。
日々成長していく(賢くなっていく)サービスですので、継続して検証確認していこうと思います。

以上