cocos2d-xにgoogle-breakpadを組み込みクラッシュ情報取得

## 詳細
cocos2d-xにgoogle-breakpadを組み込みクラッシュ情報取得する仕組みを使用するプロジェクトがあったので、内容把握のためにまとめました。
**※ 現在の主流は恐らくFirebase導入です...**

## google-breakpadについて
google-breakpadはオープンソースでマルチプラットフォームに対応したクラッシュレポーティングシステムです。下記の方法で使用することが出来ます。
https://chromium.googlesource.com/breakpad/breakpad/
Github:https://github.com/google/breakpad
```git clone https://chromium.googlesource.com/breakpad/breakpad```
## 主な機能
* 例外ハンドラの提供  

> 例外ハンドラは、呼び出された例外を処理します。 例外は、事前定義の例外またはユーザー定義の例外のいずれかです。 事前定義の例外は、実行時システムによって暗黙的（自動的）に呼び出されます。ユーザー定義の例外は、明示的に呼び出す必要があります。 例外にエラー・メッセージを関連付けることができます。

* クラッシュダンプの生成

> ソフトウェアの強制終了を再現できるファイルの収集をします。

* エラー報告

> サーバーに関する問題の説明を送信して、問題解決のために実行できる手順を検索することができます。

## google-breakpadの使用用途
アプリケーションに問題が発生したら、クラッシュダンプやアプリケーションログをまとめてサーバ側に自動的に送信することで問題の解決を行いやすくすることを目的に使用します。

このような機能がなぜ必要かと言うと、アプリケージョン開発は要件が複雑になっており、バグの無い状態でリリースすることが不可能な状態になっています。このような場合、開発者へはバグが起きても迅速に修正できるようにすることが求められます。しかし、バグ修正するためにはバグを再現することができる正確な情報が必要になります。ユーザーからバグの修正に必要な正確な情報を引き出すことは非常に難しい、そもそも正確な情報というのが難しい、ある程度情報があっても再現することは難しい。この経験は何らかのアプリケージョンを開発し、バグ報告を受けたことがあるなら分かることだろうと思います。

クラッシュダンプとそれに対応するコードのリビジョンがあれば、開発者は比較的容易にバグを追跡することが可能になります。Visual StudioでWindowsアプリケーションを開発したことのある人なら、アプリケーションのクラッシュダンプをIDEに読み込ませるとソースコードレベルでどこでクラッシュが発生位置、そのときのスタックの情報などを特定できるということをご存知でしょう。これはバグを推測するよりもはるかに簡単な作業で済みます。簡単に作業できれば、バグの修正を迅速に行える可能性が高くなり、最終的にはユーザと開発者の双方にメリットをもたらすことになります。

## google-breakpadの設定
1. depot_toolsをセットアップ
1. breakpadフォルダを作成
1. depot_toolsの`fetch`コマンドでbreakpadのソースを入手

## google-breakpadをcocos2d-xプロジェクトに組み込み
breakpadフォルダをcocos2dと同じ階層にコピーします。
Android.mkを修正します。

```
LOCAL_PATH := $(call my-dir)
 
include $(CLEAR_VARS)
 
$(call import-add-path,$(LOCAL_PATH)/../../../cocos2d)
$(call import-add-path,$(LOCAL_PATH)/../../../cocos2d/external)
$(call import-add-path,$(LOCAL_PATH)/../../../cocos2d/cocos)
$(call import-add-path,$(LOCAL_PATH)/../../../cocos2d/cocos/audio/include)
$(call import-add-path,$(LOCAL_PATH)/../../../breakpad)
 
LOCAL_MODULE := MyGame_shared
 
LOCAL_MODULE_FILENAME := libMyGame
 
LOCAL_SRC_FILES := hellocpp/main.cpp \
                   ../../../Classes/AppDelegate.cpp \
                   ../../../Classes/HelloWorldScene.cpp
 
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../../Classes
 
# _COCOS_HEADER_ANDROID_BEGIN
# _COCOS_HEADER_ANDROID_END
 
LOCAL_LDFLAGS := -Wl,--allow-multiple-definition
 
LOCAL_STATIC_LIBRARIES := cocos2dx_static
LOCAL_STATIC_LIBRARIES += breakpad_client
 
# _COCOS_LIB_ANDROID_BEGIN
# _COCOS_LIB_ANDROID_END
 
include $(BUILD_SHARED_LIBRARY)
 
$(call import-module,.)
$(call import-module,src/android/google_breakpad)
 
# _COCOS_LIB_IMPORT_ANDROID_BEGIN
# _COCOS_LIB_IMPORT_ANDROID_END
```

google-breakpadを使用するためのNativeメソッドを作成  
breakpad自体はc++で作成されています。そのため、AndroidからはJNIで操作します。
proj.android-studio\app\jni\org_cocos2dx_cpp_AppActivity.cpp にファイル作成します。

org_cocos2dx_cpp_AppActivity.cpp

```
#ifndef _Included_org_cocos2dx_cpp_AppActivity
#define _Included_org_cocos2dx_cpp_AppActivity
 
#include <jni.h>
#include <stdio.h>
#include "client/linux/handler/exception_handler.h"
#include "client/linux/handler/minidump_descriptor.h"
 
static google_breakpad::ExceptionHandler* exceptionHandler;
bool DumpCallback(const google_breakpad::MinidumpDescriptor& descriptor,
                  void* context,
                  bool succeeded) {
  printf("Dump path: %s\n", descriptor.path());
  return succeeded;
}
 
#ifdef __cplusplus
extern "C" {
#endif
 
JNIEXPORT void JNICALL Java_org_cocos2dx_cpp_AppActivity_setUpBreakpad(JNIEnv* env, jobject obj, jstring filepath) {
  const char *path = env->GetStringUTFChars(filepath, 0);
  google_breakpad::MinidumpDescriptor descriptor(path);
  exceptionHandler = new google_breakpad::ExceptionHandler(descriptor, NULL, DumpCallback, NULL, true, -1);
}
 
#ifdef __cplusplus
}
#endif
#endif
```

作成したorg_cocos2dx_cpp_AppActivity.cppをビルド対象に組み込みます。

Android.mk

```
LOCAL_SRC_FILES := hellocpp/main.cpp \
                   org_cocos2dx_cpp_AppActivity.cpp\
                   ../../../Classes/AppDelegate.cpp \
                   ../../../Classes/HelloWorldScene.cpp
```

AppActivityの修正します。

```
package org.cocos2dx.cpp;
 
import android.os.Bundle;
 
import org.cocos2dx.lib.Cocos2dxActivity;
 
public class AppActivity extends Cocos2dxActivity {
    private native void setUpBreakpad(String filepath);
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
 
        String path = "/data/data/" + getContext().getPackageName() + "/files";
        setUpBreakpad(path);
    }
}
```