30 Aug 2017
|
C++
Debug
라즈베리파이
Segmentation Fault
‘Segmentation Fault’ 오류는 잘못된 메모리 영역을 참조할 때 발생하는 오류입니다. 이 때 단순히
‘Segmentation Fault’ 오류 메세지만 출력하기 때문에 어느 부분에서 오류가 발생했는지 디버그하기가
까다롭습니다. 물론, 디버깅 툴을 통해서 callstack
을 보면서 디버그를 할 수 있으면 좋겠지만,
크로스 컴파일 환경에서는 디버깅이 쉽지 않은 경우가 대부분입니다.
다음 예제 코드는 라즈베리파이에서 돌아가는 Segmentation Fault시 Callstack을 츨력하는 코드입니다.
ARM 계열 CPU를 타켓으로 했으며, x86이나 다른 계열 CPU에서는 코드에서
caller_address = (void *) uc->uc_mcontext.arm_pc; // RIP: x86_64 specific arm_pc: ARM
부분을 수정하면 됩니다.
예제 코드
#include <stdio.h>
#include <execinfo.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <cxxabi.h>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <execinfo.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ucontext.h>
#include <unistd.h>
using namespace std;
typedef struct _sig_ucontext {
unsigned long uc_flags;
struct ucontext *uc_link;
stack_t uc_stack;
struct sigcontext uc_mcontext;
sigset_t uc_sigmask;
} sig_ucontext_t;
void crit_err_hdlr(int sig_num, siginfo_t * info, void * ucontext) {
void * array[50];
void * caller_address;
char ** messages;
int size, i;
sig_ucontext_t * uc;
uc = (sig_ucontext_t *) ucontext;
/* Get the address at the time the signal was raised */
caller_address = (void *) uc->uc_mcontext.arm_pc; // RIP: x86_64 specific arm_pc: ARM
fprintf(stderr, "\n");
if (sig_num == SIGSEGV)
printf("signal %d (%s), address is %p from %p\n", sig_num, strsignal(sig_num), info->si_addr,
(void *) caller_address);
else
printf("signal %d (%s)\n", sig_num, strsignal(sig_num));
size = backtrace(array, 50);
/* overwrite sigaction with caller's address */
array[1] = caller_address;
messages = backtrace_symbols(array, size);
/* skip first stack frame (points here) */
for (i = 1; i < size && messages != NULL; ++i) {
printf("[bt]: (%d) %s\n", i, messages[i]);
}
free(messages);
exit(EXIT_FAILURE);
}
void installSignal(int __sig) {
struct sigaction sigact;
sigact.sa_sigaction = crit_err_hdlr;
sigact.sa_flags = SA_RESTART | SA_SIGINFO;
if (sigaction(__sig, &sigact, (struct sigaction *) NULL) != 0) {
fprintf(stderr, "error setting signal handler for %d (%s)\n", __sig, strsignal(__sig));
exit(EXIT_FAILURE);
}
}
int crash() {
char *s = "hello world";
*s = 'H';
printf("Crash~!!");
return 0;
}
int foo4() {
crash();
return 0;
}
int foo3() {
foo4();
return 0;
}
int foo2() {
foo3();
return 0;
}
int foo1() {
foo2();
return 0;
}
int main(int argc, char *argv[]) {
// For crashes, SIGSEV should be enough.
installSignal(SIGSEGV);
// But, you can install handlers for other signals as well:
//installSignal(SIGBUS);
//installSignal(SIGFPE);
//installSignal(SIGILL);
//installSignal(SIGINT);
//installSignal(SIGPIPE);
//installSignal(SIGQUIT);
//installSignal(SIGTERM);
//installSignal(SIGTSTP); # This is Ctrl+Z, we need to ignore it. This is to send the process to the background (suspend).
//installSignal(SIGTTIN);
//installSignal(SIGTTOU);
//installSignal(SIGSYS);
//installSignal(SIGXCPU);
//installSignal(SIGXFSZ);
foo1();
return 0;
}
빌드시 옵션
빌드시 옵션으로 -g
옵션을 줘야 합니다. Eclipe에서 Debug 모드로 빌드할 경우 기본적으로 -g3
옵션을 주기 때문에 크게 신경쓸 필요가 없습니다.
실행 결과
실행 결과는 다음과 같습니다.
$ ./SegmentationFaultCapture
signal 11 (Segmentation fault), address is 0x10a64 from 0x108c8
[bt]: (1) ./SegmentationFaultCapture() [0x108c8]
[bt]: (2) ./SegmentationFaultCapture() [0x108c8]
[bt]: (3) ./SegmentationFaultCapture() [0x108f8]
[bt]: (4) ./SegmentationFaultCapture() [0x10910]
[bt]: (5) ./SegmentationFaultCapture() [0x10928]
[bt]: (6) ./SegmentationFaultCapture() [0x10940]
[bt]: (7) ./SegmentationFaultCapture() [0x1096c]
[bt]: (8) /lib/arm-linux-gnueabihf/libc.so.6(__libc_start_main+0x114) [0xb6c5f294]
addr2line으로 오류 위치 확인
위 결과창만으로는 아직 정확한 오류 위치를 알 수 없기 때문에 addr2line
명령어를 이용해서
결과를 조회합니다.
$ addr2line -f -e ./SegmentationFaultCapture 0x108c8
_Z5crashv
D:\Workspace\RobotLitoService\SegmentationFaultCapture\Debug/../Main.cpp:73
28 Aug 2017
|
리눅스 명령어
JDK
Ubuntu
Ubuntu에 JDK 설치하는 방법
오라클 공식 사이트에서 다운받아 설치
오라클 공식 배포 사이트에서 JDK 설치 파일을 다운받아 설치하는 방법이 있습니다. 가장 확실한 방법 중의 하나입니다.
다만, 여기에서 내려 받는 파일은 확장자가 rpm
으로 되어 있습니다. 이 경우, deb
확장자로 변경해주어야 설치를 할 수 있습니다.
rpm 파일을 deb 파일로 변경하고 나면 다음 명령어를 이용해서 JDK를 설치할 수 있습니다.
$ sudo dpkg -i jdk1.8.0-144_1.8.014401_amd64.deb
확장자 변환하지 않고 직접 설치
오라클 공식 사이트에서 tar.gz
형태의 파일을 내려 받은 뒤 직접 설치하는 방법입니다.
먼저 다음 명령어를 이용해서 압축을 해제하고 파일들을 /usr/lib/jvm
폴더 아래로 이동시켜줍니다.
$ tar zxvf jdk-8u144-linux-x64.tar.gz
$ sudo mkdir /usr/lib/jvm
$ sudo mv jdk1.8.0_144 /usr/lib/jvm
그리고 다음 명령어를 통해 명령어 등록을 해 줍니다.
$ sudo update-alternatives --install /usr/bin/java java /usr/lib/jvm/jdk1.8.0_144/bin/java 1
$ sudo update-alternatives --install /usr/bin/javac javac /usr/lib/jvm/jdk1.8.0_144/bin/javac 1
$ sudo update-alternatives --install /usr/bin/javaws javaws /usr/lib/jvm/jdk1.8.0_144/bin/javaws 1
$ sudo update-alternatives --config java
$ sudo update-alternatives --config javac
$ sudo update-alternatives --config javaws
PPA를 이용한 방법
PPA(Personal Package Archive)에서 JDK를 내려 받아 설치하는 방법입니다. apt-get
명령어를 이용한 설치라서 간편합니다.
$ sudo apt-add-repository ppa:webupd8team/java
$ sudo apt-get update
$ sudo apt-get install oracle-java8-installer
21 Aug 2017
|
Android
Location
장소 검색 텍스트 박스를 자동으로 완성하는 방법입니다. 구글에서 해당 기능을 쉽게 사용할 수 있도록
컴포넌트를 제공하고 있습니다. Place Picker와
마찬가지로 ‘Google Places API for Android’ 라이브러리에대한 Google API Key 등록을 해야 합니다.
장소 자동 완성은 크게 Fragment를 사용하는 방법과 Intent 호출을 이용하는 방법의 두 가지가 있습니다.
Fragment 사용하는 코드
public class PlaceAutocompleteActivity extends AppCompatActivity {
static final String TAG = "PlaceAutocomplete";
PlaceAutocompleteFragment autocompleteFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_place_autocomplete);
autocompleteFragment = (PlaceAutocompleteFragment)
getFragmentManager().findFragmentById(R.id.place_autocomplete_fragment);
autocompleteFragment.setOnPlaceSelectedListener(new PlaceSelectionListener() {
@Override
public void onPlaceSelected(Place place) {
Log.i(TAG, "Place: " + place.getName());
}
@Override
public void onError(Status status) {
Log.i(TAG, "An error occurred: " + status);
}
});
}
}
Intent를 이용하는 방법
public class PlaceAutocompleteActivity extends AppCompatActivity {
int PLACE_AUTOCOMPLETE_REQUEST_CODE = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_place_autocomplete);
findViewById(R.id.btn_autocomplete).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
try {
Intent intent = new PlaceAutocomplete.IntentBuilder(PlaceAutocomplete.MODE_FULLSCREEN)
.build(PlaceAutocompleteActivity.this);
startActivityForResult(intent, PLACE_AUTOCOMPLETE_REQUEST_CODE);
} catch (GooglePlayServicesRepairableException e) {
// TODO: Handle the error.
} catch (GooglePlayServicesNotAvailableException e) {
// TODO: Handle the error.
}
}
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == PLACE_AUTOCOMPLETE_REQUEST_CODE) {
if (resultCode == RESULT_OK) {
Place place = PlacePicker.getPlace(data, this);
String toastMsg = String.format("Place: %s", place.getName());
Toast.makeText(this, toastMsg, Toast.LENGTH_LONG).show();
}
}
}
}
21 Aug 2017
|
Android
Location
Place Picker를 사용하기 위해서는 Google API Key를 등록해야 합니다.
Google 라이브러리들 중 ‘Google Places API for Android’를 등록하면 됩니다.
예제 코드
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
import com.google.android.gms.location.places.Place;
import com.google.android.gms.location.places.ui.PlacePicker;
public class MainActivity extends AppCompatActivity {
int PLACE_PICKER_REQUEST = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.btn_place_picker).setOnClickListener(mOnClickListener);
}
View.OnClickListener mOnClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_place_picker:
callPlacePicker();
break;
}
}
};
void callPlacePicker() {
PlacePicker.IntentBuilder builder = new PlacePicker.IntentBuilder();
try {
startActivityForResult(builder.build(this), PLACE_PICKER_REQUEST);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == PLACE_PICKER_REQUEST) {
if (resultCode == RESULT_OK) {
Place place = PlacePicker.getPlace(data, this);
String toastMsg = String.format("Place: %s", place.getName());
Toast.makeText(this, toastMsg, Toast.LENGTH_LONG).show();
}
}
}
}
21 Aug 2017
|
Android
Android Studio
IDE
키스토어(Keystore)의 SHA-1 값이 필요한 경우가 있습니다. 보통 Keystore를 직접 생성하고
콘솔 창에서 SHA-1 값을 확인한다음 해당 Key를 이용해서 배포용 apk 파일을 만드는 경우가 많습니다.
하지만, 개발 도중에 Signed Key를 계속 적용하는 것은 번거롭기 때문에 여기서는 Android Sutdio에서
Debug 모드로 빌드할 때 사용하는 SHA-1 값을 확인하는 방법을 포스팅합니다.
Android Studio에서 SHA-1 값 확인하기

Android Studio 오른편에 보면 ‘Gradle’라는 버튼이 있습니다. Gradle 뷰안에서 SHA-1 값을 조회하기를 원하는 프로젝트를 선택한 다음 ‘Tasks → android → signingReport’를 더블 클릭하면 위 그림에서처럼 ‘Gradle Console’에 SHA-1 값이 출력됩니다.
Gradle 뷰 → 프로젝트 선택 → Tasks → android → signingReport
Google 공식 가이드
Google에서는 여기에서 디버그 모드에서의 SHA-1 값을 획득하는 방법을 잘 설명하고 있습니다.
디버그 모드에서 사용하는 Keystore는 debug.keystore
파일이며, 이 파일의 위치는
- Windows : C:\Users\your_user_name.android\
- MacOS 및 Linux : ~/.android/
입니다. 해당 위치로 이동하여 다음 커맨드를 입력하면 SHA-1 값을 획득할 수 있습니다.
- Windows : keystore” -alias androiddebugkey -storepass android -keypass android
- MacOS 및 Linux : keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android