iOS 모니터링 노트 의 시작 crash

머리말
정상 적 인 붕괴 문제 에 비해 crash 를 시작 하 는 데 발생 하 는 손실 은 훨씬 크다.정상 적 으로 발표 시스템 을 충분히 구축 하면 대부분 버 전이 출시 되 기 전에 문 제 를 발견 하고 복원 할 수 있 지만 작은 확률 의 온라인 의외 가 존재 한다.시작 crash 는 일반적으로 손해 가 심각 하고 포획 하기 어 려 운 두 가지 특징 을 동시에 가지 고 있다
시작 과정
응용 아이콘 이 사용자 가 클릭 할 때 부터 응용 프로그램 이 응답 할 수 있 을 때 까지 많은 일이 발생 했 습 니 다.정상 적 으로,비록 우 리 는 crash 모니터링 도구 가 가능 한 한 빨리 시작 되 기 를 원 하지만,접속 자 는 항상 launch 사건 이 발생 한 후에 야 도 구 를 시작 할 수 있 습 니 다.이 시간 전에 발생 하 는 붕 괴 는 crash 를 시작 하 는 것 입 니 다.다음은 launch 가 발생 할 때 까지 응용 프로그램 에 존재 하 는 crash 가 발생 할 수 있 는 단 계 를 보 여 줍 니 다.

그 중에서 initialize 의 순 서 는 더 이 를 수 있 지만 항상 load 와 launch 사이 에 있 습 니 다.그림 에서 볼 때 만약 에 우리 가 작 동 crash 를 감시 하려 면 감 시 를 시작 하 는 시간 은 load 단계 에 두 어야 가장 좋 은 모니터링 효 과 를 확보 할 수 있다.
어떻게 감시 합 니까?
가장 간단 한 방법 은 접속 자가 crash 모니터링 을 시작 하 든 말 든 저 희 는 load 방법 에서 모니터링 기능 을 직접 시작 하 는 것 입 니 다.그러나 이런 방법 은 응용 을 네 가지 위험 에 직면 하 게 할 것 이다.
A/B 와 유사 한 온라인 스위치 방안 은 모니터링 도구 에 대한 제어 능력 을 잃 었 다
  • crash 모니터링 작 동 에 붕괴 문제 가 존재 하기 때문에 응용 이 완전히 마 비 될 것 이다
  • load 단계 클래스 가 로드 되 지 않 았 습 니 다.시작 도구 과정의 재 귀 로드 로 인 한 붕 괴 는 감시 할 수 없습니다
  • 이러한 위험 점 을 종합 하여 crash 모니터링 을 시작 하 는 방안 은 이러한 조건 을 만족 시 켜 야 한다.
  • 시작 과정 은 클래스 에 의존 하지 않 고 재 귀적 로 딩 으로 인 한 crash 를 피한다
  • 과정 에 crash 가 발생 하면 로그 기록 의 안전성 을 확보 할 수 있 습 니 다
  • 최종 적 으로 모니터링 프로 세 스 도 를 얻 을 수 있 습 니 다.

    의존 하지 않 는 클래스
    클래스 에 의존 하지 않 는 다 는 것 은 감시 도구 가 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 를 성공 적 으로 감 측 했 음 을 설명 합 니 다.이 때 로그 파일 을 동기 화하 여 업무 측 에 신속하게 피드백 하여 손실 을 멈 춰 야 합 니 다.먼저 손실 정지 수단 은 모두 동기 화 방식 을 사용 하여 응용 이 계속 운행 할 수 있 도록 해 야 한다.상황 에 따라 손실 을 멈 추 는 스크롤 백 방식 은 다음 과 같다.
  • crash 가 정상 적 인 업무 수행 을 방해 하지 않 는 기능 구성 요소 에서 발생 하면 A/B 온라인 스위치 를 통 해 해당 하 는 기능 을 닫 을 수 있 습 니 다.전 제 는 기능 구성 요소 가 스위치 를 사용 하여 제어 하 는 것 입 니 다
  • 4.567917.붕괴 부위 코드 는 정상 적 인 업무 수행 을 방 해 했 지만 오류 코드 가 짧 아서 서버 를 통 해 패 치 패키지 동적 으로 오류 코드 를 복구 하려 고 시도 할 수 있 습 니 다.그러나 패 치 패 키 지 는 다른 문 제 를 도입 하지 않도록 주의해 야 합 니 다4.567917.A/B Test 와 patch 패키지 가 문 제 를 해결 하지 못 하 는 상황 에서 프로젝트 가 합 리 적 인 구성 요소 화 디자인 을 사용 하면 경로 전송 을 통 해 h5 를 사용 하여 응용 을 완성 하 는 정상 적 인 운행 을 할 수 있 습 니 다4.567917.동적 복구 수단 이 부족 하고 crash 는 정상 적 인 업무 수행 을 방해 하지 않 으 며 모든 플러그 인,보조 구성 요소 의 운행 을 중단 하 는 것 을 고려 합 니 다4.567917.동태 적 인 복원 수단 이 부족 하고 1,2,3 의 방안 을 포함한다.제3자 탈옥 시장 을 통 해 역방향 가방 을 제공 하 는 것 을 고려 하여 사용자 에 게 다운로드 설 치 를 제시 할 수 있다4.567917.동태 적 인 복원 수단 이 부족 하고 1,2,3 의 방안 을 포함한다.증발 버 전 빠 른 손실 정지,Test Flight 분할 횟수 를 사용 하여 빠 른 속도 로 사용 자 를 회복 시 킵 니 다총결산
    이상 은 이 글 의 전체 내용 입 니 다.본 논문 의 내용 이 여러분 의 학습 이나 업무 에 어느 정도 참고 학습 가치 가 있 기 를 바 랍 니 다.궁금 한 점 이 있 으 시 면 댓 글 을 남 겨 주 셔 서 저희 에 대한 지지 에 감 사 드 립 니 다.

    좋은 웹페이지 즐겨찾기