iOS 모니터링 노트 의 시작 crash
정상 적 인 붕괴 문제 에 비해 crash 를 시작 하 는 데 발생 하 는 손실 은 훨씬 크다.정상 적 으로 발표 시스템 을 충분히 구축 하면 대부분 버 전이 출시 되 기 전에 문 제 를 발견 하고 복원 할 수 있 지만 작은 확률 의 온라인 의외 가 존재 한다.시작 crash 는 일반적으로 손해 가 심각 하고 포획 하기 어 려 운 두 가지 특징 을 동시에 가지 고 있다
시작 과정
응용 아이콘 이 사용자 가 클릭 할 때 부터 응용 프로그램 이 응답 할 수 있 을 때 까지 많은 일이 발생 했 습 니 다.정상 적 으로,비록 우 리 는 crash 모니터링 도구 가 가능 한 한 빨리 시작 되 기 를 원 하지만,접속 자 는 항상 launch 사건 이 발생 한 후에 야 도 구 를 시작 할 수 있 습 니 다.이 시간 전에 발생 하 는 붕 괴 는 crash 를 시작 하 는 것 입 니 다.다음은 launch 가 발생 할 때 까지 응용 프로그램 에 존재 하 는 crash 가 발생 할 수 있 는 단 계 를 보 여 줍 니 다.
그 중에서 initialize 의 순 서 는 더 이 를 수 있 지만 항상 load 와 launch 사이 에 있 습 니 다.그림 에서 볼 때 만약 에 우리 가 작 동 crash 를 감시 하려 면 감 시 를 시작 하 는 시간 은 load 단계 에 두 어야 가장 좋 은 모니터링 효 과 를 확보 할 수 있다.
어떻게 감시 합 니까?
가장 간단 한 방법 은 접속 자가 crash 모니터링 을 시작 하 든 말 든 저 희 는 load 방법 에서 모니터링 기능 을 직접 시작 하 는 것 입 니 다.그러나 이런 방법 은 응용 을 네 가지 위험 에 직면 하 게 할 것 이다.
A/B 와 유사 한 온라인 스위치 방안 은 모니터링 도구 에 대한 제어 능력 을 잃 었 다
의존 하지 않 는 클래스
클래스 에 의존 하지 않 는 다 는 것 은 감시 도구 가 C 인 터 페 이 스 를 사용 하여 기능 을 실현 해 야 한 다 는 것 을 의미 합 니 다.귀 찮 지만 runtime 의 메커니즘 으로 인해 모든 방법 을 obbc 로 호출 하기 로 결 정 했 습 니 다.msgSend 함 수 를 입구 로 하기 때문에 이 함 수 를 hook 하여 스 택 구 조 를 호출 하고 모든 스 택 기록 을 호출 할 수 있다 면 추적 방법 을 호출 하 는 것 은 어 려 운 일이 아 닙 니 다.fishhook 은 hook 함 수 를 떨 어 뜨리 는 능력 을 제공 합 니 다:
__unused static id (*orig_objc_msgSend)(id, SEL, ...);
__attribute__((__naked__)) static void hook_Objc_msgSend() {
/// save stack data
/// push msgSend
/// resume stack data
/// call origin msgSend
/// save stack data
/// pop msgSend
/// resume stack data
}
void observe_Objc_msgSend() {
struct rebinding msgSend_rebinding = { "objc_msgSend", hook_Objc_msgSend, (void *)&orig_objc_msgSend };
rebind_symbols((struct rebinding[1]){msgSend_rebinding}, 1);
}
msgSend 실현__naked__수 식 된 함 수 는 컴 파 일 러 가 함수 호출 시 스 택 을 사용 하지 않 고 매개 변수 정 보 를 저장 하 는 동시에 함수 반환 주 소 는 LR 레지스터 에 저 장 됩 니 다.msgSend 자체 가 이 수식 자 를 사용 하기 때문에 기록 함수 가 호출 한 출입 창고 작업 에서 레지스터 데 이 터 를 저장 하고 복원 할 수 있 도록 해 야 합 니 다.msgSend 는 x0-x9 의 레지스터 를 이용 하여 매개 변수 정 보 를 저장 하고 sp 레지스터 를 수 동 으로 사용 하여 이러한 매개 변수 정 보 를 저장 하고 복원 할 수 있 습 니 다.
///
#define save() \
__asm volatile ( \
"stp x8, x9, [sp, #-16]!
" \
"stp x6, x7, [sp, #-16]!
" \
"stp x4, x5, [sp, #-16]!
" \
"stp x2, x3, [sp, #-16]!
" \
"stp x0, x1, [sp, #-16]!
");
///
#define resume() \
__asm volatile ( \
"ldp x0, x1, [sp], #16
" \
"ldp x2, x3, [sp], #16
" \
"ldp x4, x5, [sp], #16
" \
"ldp x6, x7, [sp], #16
" \
"ldp x8, x9, [sp], #16
" );
/// ,value
#define call(b, value) \
__asm volatile ("stp x8, x9, [sp, #-16]!
"); \
__asm volatile ("mov x12, %0
" :: "r"(value)); \
__asm volatile ("ldp x8, x9, [sp], #16
"); \
__asm volatile (#b " x12
");
/// msgSend
__attribute__((__naked__)) static void hook_Objc_msgSend() {
save()
__asm volatile ("mov x2, lr
");
__asm volatile ("mov x3, x4
");
call(blr, &push_msgSend)
resume()
call(blr, orig_objc_msgSend)
save()
call(blr, &pop_msgSend)
__asm volatile ("mov lr, x0
");
resume()
__asm volatile ("ret
");
}
로그 기록일반적인 I/O 처 리 는 crash 가 발생 하 는 데이터 안전 을 보장 할 수 없 기 때문에 mmap 는 이 장면 에 가장 적합 한 방안 입 니 다.mmap 는 응용 프로그램 이 거부 할 수 없 는 충돌 이 발생 했 을 때 도 파일 을 IO 에 기록 하 는 작업 을 할 수 있 도록 보장 합 니 다.또한 저 희 는 class 와 selector 의 호출 스 택 정 보 를 기록 하고 재 귀 알고리즘 이 존재 하지 않 는 상황 에서 아주 작은 메모리 만 사용 하면 이 데 이 터 를 기록 할 수 있 습 니 다.
time_t ts = time(NULL);
const char *filePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject stringByAppendingString: [NSString stringWithFormat: @"%d", ts]].UTF8String;
unsigned char *buffer = NULL;
int fileDescriptor = open(filePath, O_RDWR, 0);
buffer = (unsigned char *)mmap(NULL, MB * 4, PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED, fileDescriptor, 0);
buffer 는 우리 가 데 이 터 를 기록 하 는 버퍼 입 니 다.스 택 의 정보 가 정확 하도록 호출 함수 정보 가 스 택 에 출입 할 때마다 버퍼 의 데 이 터 를 업데이트 해 야 합 니 다.실행 가능 한 방법 은 모든 호출 기록 에@기호 접 두 사 를 추가 하 는 것 입 니 다.마지막 호출 기록 의 이 기호 아래 표 시 를 저장 하고 스 택 을 나 갈 때 이 아래 표 시 된 모든 데 이 터 를 지우 면 됩 니 다.
static inline void push_msgSend(id _self, Class _cls, SEL _cmd, uintptr_t lr) {
_lastIdx = _length;
buffer[_lastIdx] = '@';
......
}
static inline void pop_msgSend(id _self, SEL _cmd, uintptr_t lr) {
......
buffer[_lastIdx] = '\0';
_length = _lastIdx;
size_t idx = _lastIdx - 1;
while (idx >= 0) {
if (buffer[idx] == '@') {
_lastIdx = idx;
break;
}
idx--;
}
}
로그 비우 기msgSend 의 호출 이 매우 빈번 하기 때문에 이런 모니터링 방안 은 장시간 가동 하기에 적합 하지 않 기 때문에 특정한 시기 에 모니터링 을 꺼 야 한다.정상 적 인 붕괴 모니터링 이 시 작 될 때 도 crash 가 존재 할 수 있 기 때문에 becomeActive 알림 을 감청 하여 기능 을 닫 는 것 이 가장 적합 한 선택 입 니 다.이 때 는 launch 가 붕괴 모니터링 도 구 를 시작 하 는 단계 가 지 났 기 때문에 이 도구 자체 가 정상적으로 사용 되 는 것 을 보장 할 수 있 습 니 다.
[[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(closeMsgSendObserve) name: UIApplicationDidBecomeActiveNotification object: nil];
- (void)closeMsgSendObserve {
close(fileDescriptor);
munmap(buffer, MB * 4);
[[NSFileManager defaultManager] removeItemAtPath: _logPath error: nil];
}
스크롤 백스크롤 백 이 필요 할 때 시작 crash 가 발생 했 음 을 설명 합 니 다.로그 내용 에 따라 처리 방식 이 다 릅 니 다.
로그 파일 은 빈 파일 입 니 다.
이 경우 가장 위험한 상황 입 니 다.로그 파일 이 비어 있 으 면 파일 이 만 들 어 졌 음 을 설명 하지만 아직 호출 방법 이 없습니다.fishhook 처리 과정 에서 crash 가 존재 할 수 있 습 니 다.이 때 는 모니터링 방안 을 직접 닫 아야 합 니 다.원인 이 아니 더 라 도 빠 른 속도 로 버 전 을 추가 해 야 합 니 다.
로그 파일 이 비어 있 지 않 음
로그 파일 이 비어 있 지 않 으 면 crash 를 성공 적 으로 감 측 했 음 을 설명 합 니 다.이 때 로그 파일 을 동기 화하 여 업무 측 에 신속하게 피드백 하여 손실 을 멈 춰 야 합 니 다.먼저 손실 정지 수단 은 모두 동기 화 방식 을 사용 하여 응용 이 계속 운행 할 수 있 도록 해 야 한다.상황 에 따라 손실 을 멈 추 는 스크롤 백 방식 은 다음 과 같다.
이상 은 이 글 의 전체 내용 입 니 다.본 논문 의 내용 이 여러분 의 학습 이나 업무 에 어느 정도 참고 학습 가치 가 있 기 를 바 랍 니 다.궁금 한 점 이 있 으 시 면 댓 글 을 남 겨 주 셔 서 저희 에 대한 지지 에 감 사 드 립 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Swift의 패스트 패스Objective-C를 대체하기 위해 만들어졌지만 Xcode는 Objective-C 런타임 라이브러리를 사용하기 때문에 Swift와 함께 C, C++ 및 Objective-C를 컴파일할 수 있습니다. Xcode는 S...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.