Truy xuất và tải hồ sơ lên

Cho dù bạn thu thập hồ sơ do ứng dụng điều khiển hay hồ sơ dựa trên điều kiện kích hoạt, thì ProfilingManager đều lưu tệp đã hoàn tất trên thiết bị và gửi ProfilingResult đến lệnh gọi lại đã đăng ký của bạn. Gọi ProfilingResult.getResultFilePath để lấy đường dẫn của hồ sơ đã lưu:

Truy xuất hồ sơ từ một thiết bị

Phần này mô tả nơi ProfilingManager lưu trữ hồ sơ trên thiết bị, cách hoạt động của tính năng tự động dọn dẹp tệp và cách lấy tệp trên thiết bị bằng adb.

Vị trí tệp và định dạng đặt tên

ProfilingManager lưu hồ sơ đã loại bỏ thông tin nhạy cảm vào thư mục bộ nhớ dành riêng cho ứng dụng tại /data/user/0/<app>/files/profiling/. Định dạng tên tệp phụ thuộc vào cách thu thập hồ sơ:

  • Hồ sơ do ứng dụng điều khiển:

    profile_<tag>_<datetime>_uid-<uid>.<profile-type-ext>
    

    Nếu không có thẻ nào được cung cấp khi yêu cầu hồ sơ, thì <tag>_ sẽ bị bỏ qua: profile_<datetime>_uid-<uid>.<profile-type-ext>.

  • Hồ sơ dựa trên trình kích hoạt:

    profile_trigger-type-<trigger_type>_<datetime>_uid-<uid>.<profile-type-ext>
    

Trong trường hợp:

  • <app> là tên gói của ứng dụng.
  • <tag> là tối đa 20 ký tự chữ-số đầu tiên (và dấu gạch nối) của thẻ do phương thức gọi cung cấp được định cấu hình bằng setTag hoặc được truyền đến requestProfiling, được chuyển đổi thành chữ thường và xoá mọi ký tự nằm ngoài a-z, 0-9 và -.
  • <trigger_type> là mã hằng số nguyên cho điều kiện kích hoạt từ ProfilingTrigger. Luôn gọi ProfilingResult.getTriggerType trong mã thay vì phân tích cú pháp tên tệp.
  • <datetime> là ngày và giờ hồ sơ được ghi lại (yyyy-MM-dd-HH-mm-ss-SSS).
  • <uid> là mã nhận dạng người dùng Linux của ứng dụng (có trong các phiên bản mô-đun Profiling gần đây).
  • <profile-type-ext> là đuôi tệp cho loại hồ sơ đã thu thập:
    • .perfetto-trace cho dấu vết hệ thống
    • .perfetto-java-heap-dump cho tệp báo lỗi vùng nhớ Java
    • .perfetto-heap-profile cho hồ sơ ảnh chụp nhanh của vùng nhớ khối xếp
    • .perfetto-stack-sample cho hồ sơ lấy mẫu ngăn xếp

Bộ nhớ trên thiết bị và dọn dẹp

Để giải phóng bộ nhớ trên thiết bị ngay lập tức, hãy xoá từng tệp hồ sơ trong trình thực thi tải lên ngay sau khi tải lên thành công (vì quá trình dọn dẹp tự động chỉ chạy khi ProfilingManager phân phối kết quả cho ứng dụng của bạn, nếu không, các tệp chưa tải lên có thể vẫn còn trên ổ đĩa nếu ứng dụng của bạn ngừng đăng ký lệnh gọi lại sau khi kết thúc chiến dịch). Để dự phòng cho các phiên bản mô-đun Profiling gần đây, ProfilingManager cũng tự động xoá các tệp đã phân phối trong thư mục lưu trữ dành riêng cho ứng dụng của bạn (các tệp đã tồn tại hơn 5 ngày) khi kết quả mới được phân phối.

Tải hồ sơ lên trong phiên bản phát hành chính thức bằng WorkManager

Vì các tệp hồ sơ có thể có kích thước vài megabyte, nên hãy lên lịch tải bản phát hành chính thức lên trong nền khi thiết bị đang kết nối với mạng không đo lượng dữ liệu (Wi-Fi), đang sạc và ở trạng thái rảnh.

Trước khi tải lên, hãy nén tệp hồ sơ (ví dụ: sử dụng GZIPOutputStream hoặc ZipOutputStream). Hồ sơ protobuf Perfetto và tệp báo lỗi vùng nhớ khối xếp Java nén đáng kể, giúp giảm băng thông mạng và chi phí lưu trữ phụ trợ.

Thêm phần phụ thuộc WorkManager

ProfilingManager không phụ thuộc vào đám mây, vì vậy, bạn có thể lên lịch tải hồ sơ lên bất kỳ phần phụ trợ nào bằng WorkManager. Thêm thư viện WorkManager vào tệp build.gradle.kts:

Kotlin

   dependencies {
       implementation("androidx.work:work-runtime:2.12.0")
   }
   

Groovy

   dependencies {
       implementation 'androidx.work:work-runtime:2.12.0'
   }
   

Lên lịch cho một công việc tải lên

Khi ứng dụng của bạn nhận được ProfilingResult, hãy lên lịch cho công việc tải lên. Ví dụ sau đây bỏ qua yêu cầu lập hồ sơ ban đầu; để xem ví dụ về yêu cầu hoàn chỉnh, hãy xem phần Ghi lại dấu vết hệ thống.

Kotlin

class TraceUploadWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) {
    override fun doWork(): Result {
        // Perform your uploading work here
        Log.d("ProfileTest", "Uploading trace: " + inputData.getString("PROFILE_PATH"))

        return Result.success()
    }
}

fun setupProfileUploadWorker(profileFilepath: String?) {
    val workMgr = WorkManager.getInstance(applicationContext)
    val workRequestBuilder = OneTimeWorkRequest.Builder(TraceUploadWorker::class)

    val constraints = Constraints.Builder()
        .setRequiredNetworkType(NetworkType.UNMETERED)
        .setRequiresDeviceIdle(true)
        .setRequiresCharging(true)
        .build()
    workRequestBuilder.setConstraints(constraints)

    val inputDataBuilder = Data.Builder()
    inputDataBuilder.putString("PROFILE_PATH", profileFilepath)
    workRequestBuilder.setInputData(inputDataBuilder.build())

    workMgr.enqueue(workRequestBuilder.build())
}

Java

public static class TraceUploadWorker extends Worker {

  public TraceUploadWorker(
      @androidx.annotation.NonNull Context context,
      @androidx.annotation.NonNull WorkerParameters workerParams) {
    super(context, workerParams);
  }

  @androidx.annotation.NonNull
  @Override
  public Result doWork() {
    // Perform your uploading work here
    Log.d("ProfileTest", "Uploading trace: " + getInputData().getString("PROFILE_PATH"));

    return Result.success();
  }
}

public void setupProfileUploadWorker(String profileFilepath) {
  WorkManager workMgr = WorkManager.getInstance(getApplicationContext());
  OneTimeWorkRequest.Builder workRequestBuilder = new OneTimeWorkRequest.Builder(
      TraceUploadWorker.class);

  Constraints constraints = new Constraints.Builder()
      .setRequiredNetworkType(NetworkType.UNMETERED)
      .setRequiresDeviceIdle(true)
      .setRequiresCharging(true)
      .build();
  workRequestBuilder.setConstraints(constraints);

  Data.Builder inputDataBuilder = new Data.Builder();
  inputDataBuilder.putString("PROFILE_PATH", profileFilepath);
  workRequestBuilder.setInputData(inputDataBuilder.build());

  workMgr.enqueue(workRequestBuilder.build());
}

Hướng dẫn từng bước về mã

Ví dụ trước thực hiện các bước sau:

  • Xác định worker: Tạo một lớp TraceUploadWorker mở rộng Worker. Triển khai doWork để xác minh rằng tệp hồ sơ vẫn tồn tại, nén và tải tệp đó lên bằng SDK phụ trợ hoặc máy khách HTTP, sau đó xoá tệp cục bộ sau khi tải lên thành công.
  • Lên lịch cho công việc:
    • Tạo OneTimeWorkRequest cho worker.
    • Đặt quy tắc ràng buộc: Sử dụng setRequiredNetworkType(NetworkType.UNMETERED), setRequiresDeviceIdle(true) và setRequiresCharging(true) để quá trình tải lên chỉ chạy khi thiết bị đang kết nối Wi-Fi, đang sạc và ở trạng thái rảnh.
    • Truyền dữ liệu: Sử dụng setInputData để truyền đường dẫn hồ sơ từ getResultFilePath đến worker.
    • Enqueue: Gửi yêu cầu đến WorkManager bằng cách gọi WorkManager.enqueue.

Các bước tiếp theo

Sau khi truy xuất hoặc tải hồ sơ lên, hãy trực quan hoá hồ sơ trong giao diện người dùng Perfetto, truy vấn từng hồ sơ bằng PerfettoSQL, phân tích hàng loạt dấu vết hoặc phân tích hồ sơ bằng AI. Để được hướng dẫn về cách thiết lập quy trình phân tích phía máy chủ có thể mở rộng, hãy xem bài viết Triển khai Bigtrace trên Kubernetes.