Merge "ART: Remove CompiledClass"
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index fdfa4ee..503026e 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -8415,6 +8415,23 @@
}
} else if (Primitive::IsIntegralType(result_type) && Primitive::IsFloatingPointType(input_type)) {
CHECK(result_type == Primitive::kPrimInt || result_type == Primitive::kPrimLong);
+
+ // When NAN2008=1 (R6), the truncate instruction caps the output at the minimum/maximum
+ // value of the output type if the input is outside of the range after the truncation or
+ // produces 0 when the input is a NaN. IOW, the three special cases produce three distinct
+ // results. This matches the desired float/double-to-int/long conversion exactly.
+ //
+ // When NAN2008=0 (R2 and before), the truncate instruction produces the maximum positive
+ // value when the input is either a NaN or is outside of the range of the output type
+ // after the truncation. IOW, the three special cases (NaN, too small, too big) produce
+ // the same result.
+ //
+ // The code takes care of the different behaviors by first comparing the input to the
+ // minimum output value (-2**-63 for truncating to long, -2**-31 for truncating to int).
+ // If the input is greater than or equal to the minimum, it procedes to the truncate
+ // instruction, which will handle such an input the same way irrespective of NAN2008.
+ // Otherwise the input is compared to itself to determine whether it is a NaN or not
+ // in order to return either zero or the minimum value.
if (result_type == Primitive::kPrimLong) {
if (isR6) {
// trunc.l.s/trunc.l.d requires MIPSR2+ with FR=1. MIPS32R6 is implemented as a secondary
@@ -8422,62 +8439,6 @@
FRegister src = locations->InAt(0).AsFpuRegister<FRegister>();
Register dst_high = locations->Out().AsRegisterPairHigh<Register>();
Register dst_low = locations->Out().AsRegisterPairLow<Register>();
- MipsLabel truncate;
- MipsLabel done;
-
- // When NAN2008=0 (R2 and before), the truncate instruction produces the maximum positive
- // value when the input is either a NaN or is outside of the range of the output type
- // after the truncation. IOW, the three special cases (NaN, too small, too big) produce
- // the same result.
- //
- // When NAN2008=1 (R6), the truncate instruction caps the output at the minimum/maximum
- // value of the output type if the input is outside of the range after the truncation or
- // produces 0 when the input is a NaN. IOW, the three special cases produce three distinct
- // results. This matches the desired float/double-to-int/long conversion exactly.
- //
- // So, NAN2008 affects handling of negative values and NaNs by the truncate instruction.
- //
- // The following code supports both NAN2008=0 and NAN2008=1 behaviors of the truncate
- // instruction, the reason being that the emulator implements NAN2008=0 on MIPS64R6,
- // even though it must be NAN2008=1 on R6.
- //
- // The code takes care of the different behaviors by first comparing the input to the
- // minimum output value (-2**-63 for truncating to long, -2**-31 for truncating to int).
- // If the input is greater than or equal to the minimum, it procedes to the truncate
- // instruction, which will handle such an input the same way irrespective of NAN2008.
- // Otherwise the input is compared to itself to determine whether it is a NaN or not
- // in order to return either zero or the minimum value.
- //
- // TODO: simplify this when the emulator correctly implements NAN2008=1 behavior of the
- // truncate instruction for MIPS64R6.
- if (input_type == Primitive::kPrimFloat) {
- uint32_t min_val = bit_cast<uint32_t, float>(std::numeric_limits<int64_t>::min());
- __ LoadConst32(TMP, min_val);
- __ Mtc1(TMP, FTMP);
- __ CmpLeS(FTMP, FTMP, src);
- } else {
- uint64_t min_val = bit_cast<uint64_t, double>(std::numeric_limits<int64_t>::min());
- __ LoadConst32(TMP, High32Bits(min_val));
- __ Mtc1(ZERO, FTMP);
- __ Mthc1(TMP, FTMP);
- __ CmpLeD(FTMP, FTMP, src);
- }
-
- __ Bc1nez(FTMP, &truncate);
-
- if (input_type == Primitive::kPrimFloat) {
- __ CmpEqS(FTMP, src, src);
- } else {
- __ CmpEqD(FTMP, src, src);
- }
- __ Move(dst_low, ZERO);
- __ LoadConst32(dst_high, std::numeric_limits<int32_t>::min());
- __ Mfc1(TMP, FTMP);
- __ And(dst_high, dst_high, TMP);
-
- __ B(&done);
-
- __ Bind(&truncate);
if (input_type == Primitive::kPrimFloat) {
__ TruncLS(FTMP, src);
@@ -8486,8 +8447,6 @@
}
__ Mfc1(dst_low, FTMP);
__ Mfhc1(dst_high, FTMP);
-
- __ Bind(&done);
} else {
QuickEntrypointEnum entrypoint = (input_type == Primitive::kPrimFloat) ? kQuickF2l
: kQuickD2l;
@@ -8504,43 +8463,19 @@
MipsLabel truncate;
MipsLabel done;
- // The following code supports both NAN2008=0 and NAN2008=1 behaviors of the truncate
- // instruction, the reason being that the emulator implements NAN2008=0 on MIPS64R6,
- // even though it must be NAN2008=1 on R6.
- //
- // For details see the large comment above for the truncation of float/double to long on R6.
- //
- // TODO: simplify this when the emulator correctly implements NAN2008=1 behavior of the
- // truncate instruction for MIPS64R6.
- if (input_type == Primitive::kPrimFloat) {
- uint32_t min_val = bit_cast<uint32_t, float>(std::numeric_limits<int32_t>::min());
- __ LoadConst32(TMP, min_val);
- __ Mtc1(TMP, FTMP);
- } else {
- uint64_t min_val = bit_cast<uint64_t, double>(std::numeric_limits<int32_t>::min());
- __ LoadConst32(TMP, High32Bits(min_val));
- __ Mtc1(ZERO, FTMP);
- __ MoveToFpuHigh(TMP, FTMP);
- }
-
- if (isR6) {
+ if (!isR6) {
if (input_type == Primitive::kPrimFloat) {
- __ CmpLeS(FTMP, FTMP, src);
+ uint32_t min_val = bit_cast<uint32_t, float>(std::numeric_limits<int32_t>::min());
+ __ LoadConst32(TMP, min_val);
+ __ Mtc1(TMP, FTMP);
} else {
- __ CmpLeD(FTMP, FTMP, src);
+ uint64_t min_val = bit_cast<uint64_t, double>(std::numeric_limits<int32_t>::min());
+ __ LoadConst32(TMP, High32Bits(min_val));
+ __ Mtc1(ZERO, FTMP);
+ __ MoveToFpuHigh(TMP, FTMP);
}
- __ Bc1nez(FTMP, &truncate);
if (input_type == Primitive::kPrimFloat) {
- __ CmpEqS(FTMP, src, src);
- } else {
- __ CmpEqD(FTMP, src, src);
- }
- __ LoadConst32(dst, std::numeric_limits<int32_t>::min());
- __ Mfc1(TMP, FTMP);
- __ And(dst, dst, TMP);
- } else {
- if (input_type == Primitive::kPrimFloat) {
__ ColeS(0, FTMP, src);
} else {
__ ColeD(0, FTMP, src);
@@ -8554,12 +8489,12 @@
}
__ LoadConst32(dst, std::numeric_limits<int32_t>::min());
__ Movf(dst, ZERO, 0);
+
+ __ B(&done);
+
+ __ Bind(&truncate);
}
- __ B(&done);
-
- __ Bind(&truncate);
-
if (input_type == Primitive::kPrimFloat) {
__ TruncWS(FTMP, src);
} else {
@@ -8567,7 +8502,9 @@
}
__ Mfc1(dst, FTMP);
- __ Bind(&done);
+ if (!isR6) {
+ __ Bind(&done);
+ }
}
} else if (Primitive::IsFloatingPointType(result_type) &&
Primitive::IsFloatingPointType(input_type)) {
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index 8261406..e0dba21 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -5992,68 +5992,6 @@
CHECK(result_type == Primitive::kPrimInt || result_type == Primitive::kPrimLong);
GpuRegister dst = locations->Out().AsRegister<GpuRegister>();
FpuRegister src = locations->InAt(0).AsFpuRegister<FpuRegister>();
- Mips64Label truncate;
- Mips64Label done;
-
- // When NAN2008=0 (R2 and before), the truncate instruction produces the maximum positive
- // value when the input is either a NaN or is outside of the range of the output type
- // after the truncation. IOW, the three special cases (NaN, too small, too big) produce
- // the same result.
- //
- // When NAN2008=1 (R6), the truncate instruction caps the output at the minimum/maximum
- // value of the output type if the input is outside of the range after the truncation or
- // produces 0 when the input is a NaN. IOW, the three special cases produce three distinct
- // results. This matches the desired float/double-to-int/long conversion exactly.
- //
- // So, NAN2008 affects handling of negative values and NaNs by the truncate instruction.
- //
- // The following code supports both NAN2008=0 and NAN2008=1 behaviors of the truncate
- // instruction, the reason being that the emulator implements NAN2008=0 on MIPS64R6,
- // even though it must be NAN2008=1 on R6.
- //
- // The code takes care of the different behaviors by first comparing the input to the
- // minimum output value (-2**-63 for truncating to long, -2**-31 for truncating to int).
- // If the input is greater than or equal to the minimum, it procedes to the truncate
- // instruction, which will handle such an input the same way irrespective of NAN2008.
- // Otherwise the input is compared to itself to determine whether it is a NaN or not
- // in order to return either zero or the minimum value.
- //
- // TODO: simplify this when the emulator correctly implements NAN2008=1 behavior of the
- // truncate instruction for MIPS64R6.
- if (input_type == Primitive::kPrimFloat) {
- uint32_t min_val = (result_type == Primitive::kPrimLong)
- ? bit_cast<uint32_t, float>(std::numeric_limits<int64_t>::min())
- : bit_cast<uint32_t, float>(std::numeric_limits<int32_t>::min());
- __ LoadConst32(TMP, min_val);
- __ Mtc1(TMP, FTMP);
- __ CmpLeS(FTMP, FTMP, src);
- } else {
- uint64_t min_val = (result_type == Primitive::kPrimLong)
- ? bit_cast<uint64_t, double>(std::numeric_limits<int64_t>::min())
- : bit_cast<uint64_t, double>(std::numeric_limits<int32_t>::min());
- __ LoadConst64(TMP, min_val);
- __ Dmtc1(TMP, FTMP);
- __ CmpLeD(FTMP, FTMP, src);
- }
-
- __ Bc1nez(FTMP, &truncate);
-
- if (input_type == Primitive::kPrimFloat) {
- __ CmpEqS(FTMP, src, src);
- } else {
- __ CmpEqD(FTMP, src, src);
- }
- if (result_type == Primitive::kPrimLong) {
- __ LoadConst64(dst, std::numeric_limits<int64_t>::min());
- } else {
- __ LoadConst32(dst, std::numeric_limits<int32_t>::min());
- }
- __ Mfc1(TMP, FTMP);
- __ And(dst, dst, TMP);
-
- __ Bc(&done);
-
- __ Bind(&truncate);
if (result_type == Primitive::kPrimLong) {
if (input_type == Primitive::kPrimFloat) {
@@ -6070,8 +6008,6 @@
}
__ Mfc1(dst, FTMP);
}
-
- __ Bind(&done);
} else if (Primitive::IsFloatingPointType(result_type) &&
Primitive::IsFloatingPointType(input_type)) {
FpuRegister dst = locations->Out().AsFpuRegister<FpuRegister>();
diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc
index abf5b12..eb28742 100644
--- a/compiler/optimizing/intrinsics_mips.cc
+++ b/compiler/optimizing/intrinsics_mips.cc
@@ -2555,101 +2555,110 @@
Register out = locations->Out().AsRegister<Register>();
MipsLabel done;
- MipsLabel finite;
- MipsLabel add;
- // if (in.isNaN) {
- // return 0;
- // }
- //
- // out = floor.w.s(in);
- //
- // /*
- // * This "if" statement is only needed for the pre-R6 version of floor.w.s
- // * which outputs Integer.MAX_VALUE for negative numbers with magnitudes
- // * too large to fit in a 32-bit integer.
- // *
- // * Starting with MIPSR6, which always sets FCSR.NAN2008=1, negative
- // * numbers which are too large to be represented in a 32-bit signed
- // * integer will be processed by floor.w.s to output Integer.MIN_VALUE,
- // * and will no longer be processed by this "if" statement.
- // */
- // if (out == Integer.MAX_VALUE) {
- // TMP = (in < 0.0f) ? 1 : 0;
- // /*
- // * If TMP is 1, then adding it to out will wrap its value from
- // * Integer.MAX_VALUE to Integer.MIN_VALUE.
- // */
- // return out += TMP;
- // }
- //
- // /*
- // * For negative values not handled by the previous "if" statement the
- // * test here will correctly set the value of TMP.
- // */
- // TMP = ((in - out) >= 0.5f) ? 1 : 0;
- // return out += TMP;
-
- // Test for NaN.
if (IsR6()) {
- __ CmpUnS(FTMP, in, in);
- } else {
- __ CunS(in, in);
- }
+ // out = floor(in);
+ //
+ // if (out != MAX_VALUE && out != MIN_VALUE) {
+ // TMP = ((in - out) >= 0.5) ? 1 : 0;
+ // return out += TMP;
+ // }
+ // return out;
- // Return zero for NaN.
- __ Move(out, ZERO);
- if (IsR6()) {
- __ Bc1nez(FTMP, &done);
- } else {
- __ Bc1t(&done);
- }
+ // out = floor(in);
+ __ FloorWS(FTMP, in);
+ __ Mfc1(out, FTMP);
- // out = floor(in);
- __ FloorWS(FTMP, in);
- __ Mfc1(out, FTMP);
+ // if (out != MAX_VALUE && out != MIN_VALUE)
+ __ Addiu(TMP, out, 1);
+ __ Aui(TMP, TMP, 0x8000); // TMP = out + 0x8000 0001
+ // or out - 0x7FFF FFFF.
+ // IOW, TMP = 1 if out = Int.MIN_VALUE
+ // or TMP = 0 if out = Int.MAX_VALUE.
+ __ Srl(TMP, TMP, 1); // TMP = 0 if out = Int.MIN_VALUE
+ // or out = Int.MAX_VALUE.
+ __ Beqz(TMP, &done);
- if (!IsR6()) {
- __ LoadConst32(TMP, -1);
- }
+ // TMP = (0.5f <= (in - out)) ? -1 : 0;
+ __ Cvtsw(FTMP, FTMP); // Convert output of floor.w.s back to "float".
+ __ LoadConst32(AT, bit_cast<int32_t, float>(0.5f));
+ __ SubS(FTMP, in, FTMP);
+ __ Mtc1(AT, half);
- // TMP = (out = java.lang.Integer.MAX_VALUE) ? -1 : 0;
- __ LoadConst32(AT, std::numeric_limits<int32_t>::max());
- __ Bne(AT, out, &finite);
-
- __ Mtc1(ZERO, FTMP);
- if (IsR6()) {
- __ CmpLtS(FTMP, in, FTMP);
- __ Mfc1(TMP, FTMP);
- } else {
- __ ColtS(in, FTMP);
- }
-
- __ B(&add);
-
- __ Bind(&finite);
-
- // TMP = (0.5f <= (in - out)) ? -1 : 0;
- __ Cvtsw(FTMP, FTMP); // Convert output of floor.w.s back to "float".
- __ LoadConst32(AT, bit_cast<int32_t, float>(0.5f));
- __ SubS(FTMP, in, FTMP);
- __ Mtc1(AT, half);
- if (IsR6()) {
__ CmpLeS(FTMP, half, FTMP);
__ Mfc1(TMP, FTMP);
+
+ // Return out -= TMP.
+ __ Subu(out, out, TMP);
} else {
+ // if (in.isNaN) {
+ // return 0;
+ // }
+ //
+ // out = floor.w.s(in);
+ //
+ // /*
+ // * This "if" statement is only needed for the pre-R6 version of floor.w.s
+ // * which outputs Integer.MAX_VALUE for negative numbers with magnitudes
+ // * too large to fit in a 32-bit integer.
+ // */
+ // if (out == Integer.MAX_VALUE) {
+ // TMP = (in < 0.0f) ? 1 : 0;
+ // /*
+ // * If TMP is 1, then adding it to out will wrap its value from
+ // * Integer.MAX_VALUE to Integer.MIN_VALUE.
+ // */
+ // return out += TMP;
+ // }
+ //
+ // /*
+ // * For negative values not handled by the previous "if" statement the
+ // * test here will correctly set the value of TMP.
+ // */
+ // TMP = ((in - out) >= 0.5f) ? 1 : 0;
+ // return out += TMP;
+
+ MipsLabel finite;
+ MipsLabel add;
+
+ // Test for NaN.
+ __ CunS(in, in);
+
+ // Return zero for NaN.
+ __ Move(out, ZERO);
+ __ Bc1t(&done);
+
+ // out = floor(in);
+ __ FloorWS(FTMP, in);
+ __ Mfc1(out, FTMP);
+
+ __ LoadConst32(TMP, -1);
+
+ // TMP = (out = java.lang.Integer.MAX_VALUE) ? -1 : 0;
+ __ LoadConst32(AT, std::numeric_limits<int32_t>::max());
+ __ Bne(AT, out, &finite);
+
+ __ Mtc1(ZERO, FTMP);
+ __ ColtS(in, FTMP);
+
+ __ B(&add);
+
+ __ Bind(&finite);
+
+ // TMP = (0.5f <= (in - out)) ? -1 : 0;
+ __ Cvtsw(FTMP, FTMP); // Convert output of floor.w.s back to "float".
+ __ LoadConst32(AT, bit_cast<int32_t, float>(0.5f));
+ __ SubS(FTMP, in, FTMP);
+ __ Mtc1(AT, half);
__ ColeS(half, FTMP);
- }
- __ Bind(&add);
+ __ Bind(&add);
- if (!IsR6()) {
__ Movf(TMP, ZERO);
+
+ // Return out -= TMP.
+ __ Subu(out, out, TMP);
}
-
- // Return out -= TMP.
- __ Subu(out, out, TMP);
-
__ Bind(&done);
}
diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc
index 9dce59b..a476b2b 100644
--- a/compiler/optimizing/intrinsics_mips64.cc
+++ b/compiler/optimizing/intrinsics_mips64.cc
@@ -890,54 +890,14 @@
DCHECK(type == Primitive::kPrimFloat || type == Primitive::kPrimDouble);
Mips64Label done;
- Mips64Label finite;
- Mips64Label add;
- // if (in.isNaN) {
- // return 0;
- // }
- //
// out = floor(in);
//
- // /*
- // * TODO: Amend this code when emulator FCSR.NAN2008=1 bug is fixed.
- // *
- // * Starting with MIPSR6, which always sets FCSR.NAN2008=1, negative
- // * numbers which are too large to be represented in a 32-/64-bit
- // * signed integer will be processed by floor.X.Y to output
- // * Integer.MIN_VALUE/Long.MIN_VALUE, and will no longer be
- // * processed by this "if" statement.
- // *
- // * However, this bug in the 64-bit MIPS emulator causes the
- // * behavior of floor.X.Y to be the same as pre-R6 implementations
- // * of MIPS64. When that bug is fixed this logic should be amended.
- // */
- // if (out == MAX_VALUE) {
- // TMP = (in < 0.0) ? 1 : 0;
- // /*
- // * If TMP is 1, then adding it to out will wrap its value from
- // * MAX_VALUE to MIN_VALUE.
- // */
+ // if (out != MAX_VALUE && out != MIN_VALUE) {
+ // TMP = ((in - out) >= 0.5) ? 1 : 0;
// return out += TMP;
// }
- //
- // /*
- // * For negative values not handled by the previous "if" statement the
- // * test here will correctly set the value of TMP.
- // */
- // TMP = ((in - out) >= 0.5) ? 1 : 0;
- // return out += TMP;
-
- // Test for NaN.
- if (type == Primitive::kPrimDouble) {
- __ CmpUnD(FTMP, in, in);
- } else {
- __ CmpUnS(FTMP, in, in);
- }
-
- // Return zero for NaN.
- __ Move(out, ZERO);
- __ Bc1nez(FTMP, &done);
+ // return out;
// out = floor(in);
if (type == Primitive::kPrimDouble) {
@@ -948,27 +908,26 @@
__ Mfc1(out, FTMP);
}
- // TMP = (out = java.lang.Integer.MAX_VALUE) ? 1 : 0;
+ // if (out != MAX_VALUE && out != MIN_VALUE)
if (type == Primitive::kPrimDouble) {
- __ LoadConst64(AT, std::numeric_limits<int64_t>::max());
+ __ Daddiu(TMP, out, 1);
+ __ Dati(TMP, 0x8000); // TMP = out + 0x8000 0000 0000 0001
+ // or out - 0x7FFF FFFF FFFF FFFF.
+ // IOW, TMP = 1 if out = Long.MIN_VALUE
+ // or TMP = 0 if out = Long.MAX_VALUE.
+ __ Dsrl(TMP, TMP, 1); // TMP = 0 if out = Long.MIN_VALUE
+ // or out = Long.MAX_VALUE.
+ __ Beqzc(TMP, &done);
} else {
- __ LoadConst32(AT, std::numeric_limits<int32_t>::max());
+ __ Addiu(TMP, out, 1);
+ __ Aui(TMP, TMP, 0x8000); // TMP = out + 0x8000 0001
+ // or out - 0x7FFF FFFF.
+ // IOW, TMP = 1 if out = Int.MIN_VALUE
+ // or TMP = 0 if out = Int.MAX_VALUE.
+ __ Srl(TMP, TMP, 1); // TMP = 0 if out = Int.MIN_VALUE
+ // or out = Int.MAX_VALUE.
+ __ Beqzc(TMP, &done);
}
- __ Bnec(AT, out, &finite);
-
- if (type == Primitive::kPrimDouble) {
- __ Dmtc1(ZERO, FTMP);
- __ CmpLtD(FTMP, in, FTMP);
- __ Dmfc1(AT, FTMP);
- } else {
- __ Mtc1(ZERO, FTMP);
- __ CmpLtS(FTMP, in, FTMP);
- __ Mfc1(AT, FTMP);
- }
-
- __ Bc(&add);
-
- __ Bind(&finite);
// TMP = (0.5 <= (in - out)) ? -1 : 0;
if (type == Primitive::kPrimDouble) {
@@ -977,23 +936,21 @@
__ SubD(FTMP, in, FTMP);
__ Dmtc1(AT, half);
__ CmpLeD(FTMP, half, FTMP);
- __ Dmfc1(AT, FTMP);
+ __ Dmfc1(TMP, FTMP);
} else {
__ Cvtsw(FTMP, FTMP); // Convert output of floor.w.s back to "float".
__ LoadConst32(AT, bit_cast<int32_t, float>(0.5f));
__ SubS(FTMP, in, FTMP);
__ Mtc1(AT, half);
__ CmpLeS(FTMP, half, FTMP);
- __ Mfc1(AT, FTMP);
+ __ Mfc1(TMP, FTMP);
}
- __ Bind(&add);
-
// Return out -= TMP.
if (type == Primitive::kPrimDouble) {
- __ Dsubu(out, out, AT);
+ __ Dsubu(out, out, TMP);
} else {
- __ Subu(out, out, AT);
+ __ Subu(out, out, TMP);
}
__ Bind(&done);
diff --git a/dexlayout/dexlayout_test.cc b/dexlayout/dexlayout_test.cc
index 5a6a20d..877ea92 100644
--- a/dexlayout/dexlayout_test.cc
+++ b/dexlayout/dexlayout_test.cc
@@ -41,7 +41,7 @@
"AAAAdQEAAAAQAAABAAAAjAEAAA==";
static const char kDexFileLayoutInputProfile[] =
- "cHJvADAwNwAAAAAAAAgAAAB4AQMAAAAAAQ==";
+ "cHJvADAwNQABCwABAAAAAAD1KW3+Y2xhc3Nlcy5kZXgBAA==";
// Dex file with catch handler unreferenced by try blocks.
// Constructed by building a dex file with try/catch blocks and hex editing.
diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc
index d922bc4..0acce1e 100644
--- a/runtime/jit/profile_compilation_info.cc
+++ b/runtime/jit/profile_compilation_info.cc
@@ -18,18 +18,11 @@
#include "errno.h"
#include <limits.h>
-#include <string>
#include <vector>
#include <stdlib.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/uio.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <zlib.h>
-#include <base/time_utils.h>
#include "base/mutex.h"
#include "base/scoped_flock.h"
@@ -40,14 +33,13 @@
#include "os.h"
#include "safe_map.h"
#include "utils.h"
-#include "android-base/file.h"
namespace art {
const uint8_t ProfileCompilationInfo::kProfileMagic[] = { 'p', 'r', 'o', '\0' };
-// Last profile version: Instead of method index, put the difference with the last
-// method's index.
-const uint8_t ProfileCompilationInfo::kProfileVersion[] = { '0', '0', '7', '\0' };
+// Last profile version: fix profman merges. Update profile version to force
+// regeneration of possibly faulty profiles.
+const uint8_t ProfileCompilationInfo::kProfileVersion[] = { '0', '0', '5', '\0' };
static constexpr uint16_t kMaxDexFileKeyLength = PATH_MAX;
@@ -217,12 +209,12 @@
/**
* Serialization format:
- * magic,version,number_of_dex_files,uncompressed_size_of_zipped_data,compressed_data_size,
- * zipped[dex_location1,number_of_classes1,methods_region_size,dex_location_checksum1, \
+ * magic,version,number_of_dex_files
+ * dex_location1,number_of_classes1,methods_region_size,dex_location_checksum1, \
* method_encoding_11,method_encoding_12...,class_id1,class_id2...
* dex_location2,number_of_classes2,methods_region_size,dex_location_checksum2, \
* method_encoding_21,method_encoding_22...,,class_id1,class_id2...
- * .....]
+ * .....
* The method_encoding is:
* method_id,number_of_inline_caches,inline_cache1,inline_cache2...
* The inline_cache is:
@@ -236,53 +228,28 @@
* When present, there will be no class ids following.
**/
bool ProfileCompilationInfo::Save(int fd) {
- uint64_t start = NanoTime();
ScopedTrace trace(__PRETTY_FUNCTION__);
DCHECK_GE(fd, 0);
+ // Cache at most 50KB before writing.
+ static constexpr size_t kMaxSizeToKeepBeforeWriting = 50 * KB;
// Use a vector wrapper to avoid keeping track of offsets when we add elements.
std::vector<uint8_t> buffer;
- if (!WriteBuffer(fd, kProfileMagic, sizeof(kProfileMagic))) {
- return false;
- }
- if (!WriteBuffer(fd, kProfileVersion, sizeof(kProfileVersion))) {
- return false;
- }
+ WriteBuffer(fd, kProfileMagic, sizeof(kProfileMagic));
+ WriteBuffer(fd, kProfileVersion, sizeof(kProfileVersion));
DCHECK_LE(info_.size(), std::numeric_limits<uint8_t>::max());
AddUintToBuffer(&buffer, static_cast<uint8_t>(info_.size()));
- uint32_t required_capacity = 0;
- for (const DexFileData* dex_data_ptr : info_) {
- const DexFileData& dex_data = *dex_data_ptr;
- uint32_t methods_region_size = GetMethodsRegionSize(dex_data);
- required_capacity += kLineHeaderSize +
- dex_data.profile_key.size() +
- sizeof(uint16_t) * dex_data.class_set.size() +
- methods_region_size;
- }
- if (required_capacity > kProfileSizeErrorThresholdInBytes) {
- LOG(ERROR) << "Profile data size exceeds "
- << std::to_string(kProfileSizeErrorThresholdInBytes)
- << " bytes. Profile will not be written to disk.";
- return false;
- }
- if (required_capacity > kProfileSizeWarningThresholdInBytes) {
- LOG(WARNING) << "Profile data size exceeds "
- << std::to_string(kProfileSizeWarningThresholdInBytes);
- }
- AddUintToBuffer(&buffer, required_capacity);
- if (!WriteBuffer(fd, buffer.data(), buffer.size())) {
- return false;
- }
- // Make sure that the buffer has enough capacity to avoid repeated resizings
- // while we add data.
- buffer.reserve(required_capacity);
- buffer.clear();
-
// Dex files must be written in the order of their profile index. This
// avoids writing the index in the output file and simplifies the parsing logic.
for (const DexFileData* dex_data_ptr : info_) {
const DexFileData& dex_data = *dex_data_ptr;
+ if (buffer.size() > kMaxSizeToKeepBeforeWriting) {
+ if (!WriteBuffer(fd, buffer.data(), buffer.size())) {
+ return false;
+ }
+ buffer.clear();
+ }
// Note that we allow dex files without any methods or classes, so that
// inline caches can refer valid dex files.
@@ -292,8 +259,16 @@
return false;
}
+ // Make sure that the buffer has enough capacity to avoid repeated resizings
+ // while we add data.
uint32_t methods_region_size = GetMethodsRegionSize(dex_data);
+ size_t required_capacity = buffer.size() +
+ kLineHeaderSize +
+ dex_data.profile_key.size() +
+ sizeof(uint16_t) * dex_data.class_set.size() +
+ methods_region_size;
+ buffer.reserve(required_capacity);
DCHECK_LE(dex_data.profile_key.size(), std::numeric_limits<uint16_t>::max());
DCHECK_LE(dex_data.class_set.size(), std::numeric_limits<uint16_t>::max());
AddUintToBuffer(&buffer, static_cast<uint16_t>(dex_data.profile_key.size()));
@@ -303,49 +278,19 @@
AddStringToBuffer(&buffer, dex_data.profile_key);
- uint16_t last_method_index = 0;
for (const auto& method_it : dex_data.method_map) {
- // Store the difference between the method indices. The SafeMap is ordered by
- // method_id, so the difference will always be non negative.
- DCHECK_GE(method_it.first, last_method_index);
- uint16_t diff_with_last_method_index = method_it.first - last_method_index;
- last_method_index = method_it.first;
- AddUintToBuffer(&buffer, diff_with_last_method_index);
+ AddUintToBuffer(&buffer, method_it.first);
AddInlineCacheToBuffer(&buffer, method_it.second);
}
-
- uint16_t last_class_index = 0;
for (const auto& class_id : dex_data.class_set) {
- // Store the difference between the class indices. The set is ordered by
- // class_id, so the difference will always be non negative.
- DCHECK_GE(class_id.index_, last_class_index);
- uint16_t diff_with_last_class_index = class_id.index_ - last_class_index;
- last_class_index = class_id.index_;
- AddUintToBuffer(&buffer, diff_with_last_class_index);
+ AddUintToBuffer(&buffer, class_id.index_);
}
+
+ DCHECK_LE(required_capacity, buffer.size())
+ << "Failed to add the expected number of bytes in the buffer";
}
- uint32_t output_size = 0;
- std::unique_ptr<uint8_t[]> compressed_buffer = DeflateBuffer(buffer.data(),
- required_capacity,
- &output_size);
-
- buffer.clear();
- AddUintToBuffer(&buffer, output_size);
-
- if (!WriteBuffer(fd, buffer.data(), buffer.size())) {
- return false;
- }
- if (!WriteBuffer(fd, compressed_buffer.get(), output_size)) {
- return false;
- }
- uint64_t total_time = NanoTime() - start;
- VLOG(profiler) << "Compressed from "
- << std::to_string(required_capacity)
- << " to "
- << std::to_string(output_size);
- VLOG(profiler) << "Time to save profile: " << std::to_string(total_time);
- return true;
+ return WriteBuffer(fd, buffer.data(), buffer.size());
}
void ProfileCompilationInfo::AddInlineCacheToBuffer(std::vector<uint8_t>* buffer,
@@ -639,60 +584,33 @@
uint8_t number_of_dex_files,
const ProfileLineHeader& line_header,
/*out*/std::string* error) {
- uint32_t unread_bytes_before_operation = buffer.CountUnreadBytes();
- if (unread_bytes_before_operation < line_header.method_region_size_bytes) {
- *error += "Profile EOF reached prematurely for ReadMethod";
- return kProfileLoadBadData;
- }
- size_t expected_unread_bytes_after_operation = buffer.CountUnreadBytes()
- - line_header.method_region_size_bytes;
- uint16_t last_method_index = 0;
- while (buffer.CountUnreadBytes() > expected_unread_bytes_after_operation) {
+ while (buffer.HasMoreData()) {
DexFileData* const data = GetOrAddDexFileData(line_header.dex_location, line_header.checksum);
- uint16_t diff_with_last_method_index;
- READ_UINT(uint16_t, buffer, diff_with_last_method_index, error);
- uint16_t method_index = last_method_index + diff_with_last_method_index;
- last_method_index = method_index;
+ uint16_t method_index;
+ READ_UINT(uint16_t, buffer, method_index, error);
+
auto it = data->method_map.FindOrAdd(method_index);
if (!ReadInlineCache(buffer, number_of_dex_files, &(it->second), error)) {
return false;
}
}
- uint32_t total_bytes_read = unread_bytes_before_operation - buffer.CountUnreadBytes();
- if (total_bytes_read != line_header.method_region_size_bytes) {
- *error += "Profile data inconsistent for ReadMethods";
- return false;
- }
+
return true;
}
bool ProfileCompilationInfo::ReadClasses(SafeBuffer& buffer,
+ uint16_t classes_to_read,
const ProfileLineHeader& line_header,
/*out*/std::string* error) {
- size_t unread_bytes_before_op = buffer.CountUnreadBytes();
- if (unread_bytes_before_op < line_header.class_set_size) {
- *error += "Profile EOF reached prematurely for ReadClasses";
- return kProfileLoadBadData;
- }
-
- uint16_t last_class_index = 0;
- for (uint16_t i = 0; i < line_header.class_set_size; i++) {
- uint16_t diff_with_last_class_index;
- READ_UINT(uint16_t, buffer, diff_with_last_class_index, error);
- uint16_t type_index = last_class_index + diff_with_last_class_index;
- last_class_index = type_index;
+ for (uint16_t i = 0; i < classes_to_read; i++) {
+ uint16_t type_index;
+ READ_UINT(uint16_t, buffer, type_index, error);
if (!AddClassIndex(line_header.dex_location,
line_header.checksum,
dex::TypeIndex(type_index))) {
return false;
}
}
- size_t total_bytes_read = unread_bytes_before_op - buffer.CountUnreadBytes();
- uint32_t expected_bytes_read = line_header.class_set_size * sizeof(uint16_t);
- if (total_bytes_read != expected_bytes_read) {
- *error += "Profile data inconsistent for ReadClasses";
- return false;
- }
return true;
}
@@ -732,11 +650,15 @@
return false;
}
+bool ProfileCompilationInfo::SafeBuffer::HasMoreData() {
+ return ptr_current_ < ptr_end_;
+}
+
ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::SafeBuffer::FillFromFd(
int fd,
const std::string& source,
/*out*/std::string* error) {
- size_t byte_count = (ptr_end_ - ptr_current_) * sizeof(*ptr_current_);
+ size_t byte_count = ptr_end_ - ptr_current_;
uint8_t* buffer = ptr_current_;
while (byte_count > 0) {
int bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer, byte_count));
@@ -753,31 +675,15 @@
return kProfileLoadSuccess;
}
-size_t ProfileCompilationInfo::SafeBuffer::CountUnreadBytes() {
- return (ptr_end_ - ptr_current_) * sizeof(*ptr_current_);
-}
-
-const uint8_t* ProfileCompilationInfo::SafeBuffer::GetCurrentPtr() {
- return ptr_current_;
-}
-
-void ProfileCompilationInfo::SafeBuffer::Advance(size_t data_size) {
- ptr_current_ += data_size;
-}
-
ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::ReadProfileHeader(
int fd,
/*out*/uint8_t* number_of_dex_files,
- /*out*/uint32_t* uncompressed_data_size,
- /*out*/uint32_t* compressed_data_size,
/*out*/std::string* error) {
// Read magic and version
const size_t kMagicVersionSize =
sizeof(kProfileMagic) +
sizeof(kProfileVersion) +
- sizeof(uint8_t) + // number of dex files
- sizeof(uint32_t) + // size of uncompressed profile data
- sizeof(uint32_t); // size of compressed profile data
+ sizeof(uint8_t); // number of dex files
SafeBuffer safe_buffer(kMagicVersionSize);
@@ -798,14 +704,6 @@
*error = "Cannot read the number of dex files";
return kProfileLoadBadData;
}
- if (!safe_buffer.ReadUintAndAdvance<uint32_t>(uncompressed_data_size)) {
- *error = "Cannot read the size of uncompressed data";
- return kProfileLoadBadData;
- }
- if (!safe_buffer.ReadUintAndAdvance<uint32_t>(compressed_data_size)) {
- *error = "Cannot read the size of compressed data";
- return kProfileLoadBadData;
- }
return kProfileLoadSuccess;
}
@@ -821,16 +719,17 @@
}
ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::ReadProfileLineHeader(
- SafeBuffer& buffer,
- /*out*/ProfileLineHeader* line_header,
- /*out*/std::string* error) {
- if (buffer.CountUnreadBytes() < kLineHeaderSize) {
- *error += "Profile EOF reached prematurely for ReadProfileLineHeader";
- return kProfileLoadBadData;
+ int fd,
+ /*out*/ProfileLineHeader* line_header,
+ /*out*/std::string* error) {
+ SafeBuffer header_buffer(kLineHeaderSize);
+ ProfileLoadSatus status = header_buffer.FillFromFd(fd, "ReadProfileLineHeader", error);
+ if (status != kProfileLoadSuccess) {
+ return status;
}
uint16_t dex_location_size;
- if (!ReadProfileLineHeaderElements(buffer, &dex_location_size, line_header, error)) {
+ if (!ReadProfileLineHeaderElements(header_buffer, &dex_location_size, line_header, error)) {
return kProfileLoadBadData;
}
@@ -840,19 +739,18 @@
return kProfileLoadBadData;
}
- if (buffer.CountUnreadBytes() < dex_location_size) {
- *error += "Profile EOF reached prematurely for ReadProfileHeaderDexLocation";
- return kProfileLoadBadData;
+ SafeBuffer location_buffer(dex_location_size);
+ status = location_buffer.FillFromFd(fd, "ReadProfileHeaderDexLocation", error);
+ if (status != kProfileLoadSuccess) {
+ return status;
}
- const uint8_t* base_ptr = buffer.GetCurrentPtr();
line_header->dex_location.assign(
- reinterpret_cast<const char*>(base_ptr), dex_location_size);
- buffer.Advance(dex_location_size);
+ reinterpret_cast<char*>(location_buffer.Get()), dex_location_size);
return kProfileLoadSuccess;
}
ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::ReadProfileLine(
- SafeBuffer& buffer,
+ int fd,
uint8_t number_of_dex_files,
const ProfileLineHeader& line_header,
/*out*/std::string* error) {
@@ -862,13 +760,29 @@
return kProfileLoadBadData;
}
- if (!ReadMethods(buffer, number_of_dex_files, line_header, error)) {
- return kProfileLoadBadData;
+ {
+ SafeBuffer buffer(line_header.method_region_size_bytes);
+ ProfileLoadSatus status = buffer.FillFromFd(fd, "ReadProfileLineMethods", error);
+ if (status != kProfileLoadSuccess) {
+ return status;
+ }
+
+ if (!ReadMethods(buffer, number_of_dex_files, line_header, error)) {
+ return kProfileLoadBadData;
+ }
}
- if (!ReadClasses(buffer, line_header, error)) {
- return kProfileLoadBadData;
+ {
+ SafeBuffer buffer(sizeof(uint16_t) * line_header.class_set_size);
+ ProfileLoadSatus status = buffer.FillFromFd(fd, "ReadProfileLineClasses", error);
+ if (status != kProfileLoadSuccess) {
+ return status;
+ }
+ if (!ReadClasses(buffer, line_header.class_set_size, line_header, error)) {
+ return kProfileLoadBadData;
+ }
}
+
return kProfileLoadSuccess;
}
@@ -907,135 +821,39 @@
}
// Read profile header: magic + version + number_of_dex_files.
uint8_t number_of_dex_files;
- uint32_t uncompressed_data_size;
- uint32_t compressed_data_size;
- ProfileLoadSatus status = ReadProfileHeader(fd,
- &number_of_dex_files,
- &uncompressed_data_size,
- &compressed_data_size,
- error);
-
- if (uncompressed_data_size > kProfileSizeErrorThresholdInBytes) {
- LOG(ERROR) << "Profile data size exceeds "
- << std::to_string(kProfileSizeErrorThresholdInBytes)
- << " bytes";
- return kProfileLoadBadData;
- }
- if (uncompressed_data_size > kProfileSizeWarningThresholdInBytes) {
- LOG(WARNING) << "Profile data size exceeds "
- << std::to_string(kProfileSizeWarningThresholdInBytes)
- << " bytes";
- }
-
+ ProfileLoadSatus status = ReadProfileHeader(fd, &number_of_dex_files, error);
if (status != kProfileLoadSuccess) {
return status;
}
- std::unique_ptr<uint8_t[]> compressed_data(new uint8_t[compressed_data_size]);
- bool bytes_read_success =
- android::base::ReadFully(fd, compressed_data.get(), compressed_data_size);
-
- if (testEOF(fd) != 0) {
- *error += "Unexpected data in the profile file.";
- return kProfileLoadBadData;
- }
-
- if (!bytes_read_success) {
- *error += "Unable to read compressed profile data";
- return kProfileLoadBadData;
- }
-
- SafeBuffer uncompressed_data(uncompressed_data_size);
-
- int ret = InflateBuffer(compressed_data.get(),
- compressed_data_size,
- uncompressed_data_size,
- uncompressed_data.Get());
-
- if (ret != Z_STREAM_END) {
- *error += "Error reading uncompressed profile data";
- return kProfileLoadBadData;
- }
-
for (uint8_t k = 0; k < number_of_dex_files; k++) {
ProfileLineHeader line_header;
// First, read the line header to get the amount of data we need to read.
- status = ReadProfileLineHeader(uncompressed_data, &line_header, error);
+ status = ReadProfileLineHeader(fd, &line_header, error);
if (status != kProfileLoadSuccess) {
return status;
}
// Now read the actual profile line.
- status = ReadProfileLine(uncompressed_data, number_of_dex_files, line_header, error);
+ status = ReadProfileLine(fd, number_of_dex_files, line_header, error);
if (status != kProfileLoadSuccess) {
return status;
}
}
// Check that we read everything and that profiles don't contain junk data.
- if (uncompressed_data.CountUnreadBytes() > 0) {
+ int result = testEOF(fd);
+ if (result == 0) {
+ return kProfileLoadSuccess;
+ } else if (result < 0) {
+ return kProfileLoadIOError;
+ } else {
*error = "Unexpected content in the profile file";
return kProfileLoadBadData;
- } else {
- return kProfileLoadSuccess;
}
}
-std::unique_ptr<uint8_t[]> ProfileCompilationInfo::DeflateBuffer(const uint8_t* in_buffer,
- uint32_t in_size,
- uint32_t* compressed_data_size) {
- z_stream strm;
- strm.zalloc = Z_NULL;
- strm.zfree = Z_NULL;
- strm.opaque = Z_NULL;
- int ret = deflateInit(&strm, 1);
- if (ret != Z_OK) {
- return nullptr;
- }
-
- uint32_t out_size = deflateBound(&strm, in_size);
-
- std::unique_ptr<uint8_t[]> compressed_buffer(new uint8_t[out_size]);
- strm.avail_in = in_size;
- strm.next_in = const_cast<uint8_t*>(in_buffer);
- strm.avail_out = out_size;
- strm.next_out = &compressed_buffer[0];
- ret = deflate(&strm, Z_FINISH);
- if (ret == Z_STREAM_ERROR) {
- return nullptr;
- }
- *compressed_data_size = out_size - strm.avail_out;
- deflateEnd(&strm);
- return compressed_buffer;
-}
-
-int ProfileCompilationInfo::InflateBuffer(const uint8_t* in_buffer,
- uint32_t in_size,
- uint32_t expected_uncompressed_data_size,
- uint8_t* out_buffer) {
- z_stream strm;
-
- /* allocate inflate state */
- strm.zalloc = Z_NULL;
- strm.zfree = Z_NULL;
- strm.opaque = Z_NULL;
- strm.avail_in = in_size;
- strm.next_in = const_cast<uint8_t*>(in_buffer);
- strm.avail_out = expected_uncompressed_data_size;
- strm.next_out = out_buffer;
-
- int ret;
- inflateInit(&strm);
- ret = inflate(&strm, Z_NO_FLUSH);
-
- if (strm.avail_in != 0 || strm.avail_out != 0) {
- return Z_DATA_ERROR;
- }
- inflateEnd(&strm);
- return ret;
-}
-
bool ProfileCompilationInfo::MergeWith(const ProfileCompilationInfo& other) {
// First verify that all checksums match. This will avoid adding garbage to
// the current profile info.
diff --git a/runtime/jit/profile_compilation_info.h b/runtime/jit/profile_compilation_info.h
index 9e47cc1..f68ed5d 100644
--- a/runtime/jit/profile_compilation_info.h
+++ b/runtime/jit/profile_compilation_info.h
@@ -284,9 +284,6 @@
kProfileLoadSuccess
};
- const uint32_t kProfileSizeWarningThresholdInBytes = 500000U;
- const uint32_t kProfileSizeErrorThresholdInBytes = 1000000U;
-
// Internal representation of the profile information belonging to a dex file.
// Note that we could do without profile_key (the key used to encode the dex
// file in the profile) and profile_index (the index of the dex file in the
@@ -356,21 +353,6 @@
// Checks if the profile is empty.
bool IsEmpty() const;
- // Inflate the input buffer (in_buffer) of size in_size. It returns a buffer of
- // compressed data for the input buffer of "compressed_data_size" size.
- std::unique_ptr<uint8_t[]> DeflateBuffer(const uint8_t* in_buffer,
- uint32_t in_size,
- /*out*/uint32_t* compressed_data_size);
-
- // Inflate the input buffer(in_buffer) of size in_size. out_size is the expected output
- // size of the buffer. It puts the output in out_buffer. It returns Z_STREAM_END on
- // success. On error, it returns Z_STREAM_ERROR if the compressed data is inconsistent
- // and Z_DATA_ERROR if the stream ended prematurely or the stream has extra data.
- int InflateBuffer(const uint8_t* in_buffer,
- uint32_t in_size,
- uint32_t out_size,
- /*out*/uint8_t* out_buffer);
-
// Parsing functionality.
// The information present in the header of each profile line.
@@ -394,10 +376,6 @@
const std::string& source,
/*out*/std::string* error);
- ProfileLoadSatus FillFromBuffer(uint8_t* buffer_ptr,
- const std::string& source,
- /*out*/std::string* error);
-
// Reads an uint value (high bits to low bits) and advances the current pointer
// with the number of bits read.
template <typename T> bool ReadUintAndAdvance(/*out*/ T* value);
@@ -406,22 +384,16 @@
// equal it advances the current pointer by data_size.
bool CompareAndAdvance(const uint8_t* data, size_t data_size);
- // Advances current pointer by data_size.
- void Advance(size_t data_size);
-
- // Returns the count of unread bytes.
- size_t CountUnreadBytes();
-
- // Returns the current pointer.
- const uint8_t* GetCurrentPtr();
+ // Returns true if the buffer has more data to read.
+ bool HasMoreData();
// Get the underlying raw buffer.
uint8_t* Get() { return storage_.get(); }
private:
std::unique_ptr<uint8_t[]> storage_;
- uint8_t* ptr_end_;
uint8_t* ptr_current_;
+ uint8_t* ptr_end_;
};
// Entry point for profile loding functionality.
@@ -431,12 +403,10 @@
// lines into number_of_dex_files.
ProfileLoadSatus ReadProfileHeader(int fd,
/*out*/uint8_t* number_of_dex_files,
- /*out*/uint32_t* size_uncompressed_data,
- /*out*/uint32_t* size_compressed_data,
/*out*/std::string* error);
// Read the header of a profile line from the given fd.
- ProfileLoadSatus ReadProfileLineHeader(SafeBuffer& buffer,
+ ProfileLoadSatus ReadProfileLineHeader(int fd,
/*out*/ProfileLineHeader* line_header,
/*out*/std::string* error);
@@ -447,13 +417,14 @@
/*out*/std::string* error);
// Read a single profile line from the given fd.
- ProfileLoadSatus ReadProfileLine(SafeBuffer& buffer,
+ ProfileLoadSatus ReadProfileLine(int fd,
uint8_t number_of_dex_files,
const ProfileLineHeader& line_header,
/*out*/std::string* error);
// Read all the classes from the buffer into the profile `info_` structure.
bool ReadClasses(SafeBuffer& buffer,
+ uint16_t classes_to_read,
const ProfileLineHeader& line_header,
/*out*/std::string* error);