Skip to content

Commit

Permalink
DAML profiler: Make output user friendlier (digital-asset#6226)
Browse files Browse the repository at this point in the history
In order to make the flamegraphs easier to consume by humans, we change
the profiler output in the following ways:

1. Don't print package ids anymore. They are not particularly useful
   but cause a lot of noise.
2. Remove a few useless angle bracket and move the printed names of
   DAML-LF closer to their surface level names.
3. Unmangle identifiers on a best effort basis.
4. Give the profiles shorter names such that they don't occupy the
   whole screen and leave some space for the navigation buttons of the
   Speedscope UI.

CHANGELOG_BEGIN
CHANGELOG_END
  • Loading branch information
hurryabit authored Jun 4, 2020
1 parent 4a176c4 commit 6d71475
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -364,9 +364,11 @@ final class Engine {
profileDir match {
case None => ()
case Some(profileDir) =>
val profileName = Engine.profileName(t, meta)
machine.profile.name = profileName
val profileFile = profileDir.resolve(Paths.get(profileName))
val hash = meta.nodeSeeds(0)._2.toHexString
val desc = Engine.profileDesc(t)
machine.profile.name = s"${desc}-${hash.substring(0, 6)}"
val profileFile =
profileDir.resolve(Paths.get(s"${meta.submissionTime}-${desc}-${hash}.json"))
machine.profile.writeSpeedscopeJson(profileFile)
}
ResultDone((t, meta))
Expand Down Expand Up @@ -408,22 +410,19 @@ object Engine {
InitialSeeding.TransactionSeed(
crypto.Hash.deriveTransactionSeed(submissionSeed, participant, submissionTime))

private def profileName(tx: Tx.Transaction, meta: Tx.Metadata): String = {
val hash = meta.nodeSeeds(0)._2.toHexString
val desc =
if (tx.roots.length == 1) {
val makeDesc = (kind: String, tmpl: Ref.Identifier, extra: Option[String]) =>
s"${kind}:${tmpl.qualifiedName.name}${extra.map(extra => s":${extra}").getOrElse("")}"
tx.nodes.get(tx.roots(0)).toList.head match {
case create: NodeCreate[_, _] => makeDesc("create", create.coinst.template, None)
case exercise: NodeExercises[_, _, _] =>
makeDesc("exercise", exercise.templateId, Some(exercise.choiceId.toString))
case fetch: NodeFetch[_, _] => makeDesc("fetch", fetch.templateId, None)
case lookup: NodeLookupByKey[_, _] => makeDesc("lookup", lookup.templateId, None)
}
} else {
s"compound:${tx.roots.length}"
private def profileDesc(tx: Tx.Transaction): String = {
if (tx.roots.length == 1) {
val makeDesc = (kind: String, tmpl: Ref.Identifier, extra: Option[String]) =>
s"${kind}:${tmpl.qualifiedName.name}${extra.map(extra => s":${extra}").getOrElse("")}"
tx.nodes.get(tx.roots(0)).toList.head match {
case create: NodeCreate[_, _] => makeDesc("create", create.coinst.template, None)
case exercise: NodeExercises[_, _, _] =>
makeDesc("exercise", exercise.templateId, Some(exercise.choiceId.toString))
case fetch: NodeFetch[_, _] => makeDesc("fetch", fetch.templateId, None)
case lookup: NodeLookupByKey[_, _] => makeDesc("lookup", lookup.templateId, None)
}
s"${meta.submissionTime}-${desc}-${hash}.json"
} else {
s"compound:${tx.roots.length}"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -599,7 +599,7 @@ private[lf] final case class Compiler(
SELet(encodeKeyWithMaintainers(key, keyTemplate)) in {
env = env.incrPos // key with maintainers
withLabel(
s"<fetch_by_key ${retrieveByKey.templateId}",
s"fetchByKey @${retrieveByKey.templateId.qualifiedName}",
SEAbs(1) {
env = env.incrPos // token
env = env.addExprVar(template.param)
Expand Down Expand Up @@ -713,7 +713,7 @@ private[lf] final case class Compiler(
env = env.incrPos // $beginCommit
SELet(party, update) in
withLabel(
"<submit>",
"submit",
SEAbs(1) {
SELet(
// stack: <party> <update> <token>
Expand All @@ -738,7 +738,7 @@ private[lf] final case class Compiler(
env = env.incrPos // $beginCommit
val update = translate(updateE)
withLabel(
"<submit_must_fail>",
"submitMustFail",
SEAbs(1) {
SELet(
SBSBeginCommit(optLoc)(party, SEVar(1)),
Expand All @@ -755,7 +755,7 @@ private[lf] final case class Compiler(
withEnv { _ =>
env = env.incrPos // token
withLabel(
"<get_party>",
"getParty",
SEAbs(1) {
SBSGetParty(translate(e), SEVar(1))
}
Expand All @@ -766,7 +766,7 @@ private[lf] final case class Compiler(
withEnv { _ =>
env = env.incrPos // token
withLabel(
"<pass>",
"pass",
SEAbs(1) {
SBSPass(translate(relTimeE), SEVar(1))
}
Expand Down Expand Up @@ -887,7 +887,7 @@ private[lf] final case class Compiler(
env = env.addExprVar(choice.selfBinder, selfBinderPos)
val update = translate(choice.update)
withLabel(
s"<exercise ${tmplId}:${cname}>",
s"exercise @${tmplId.qualifiedName} ${cname}",
SEAbs(5) {
SELet(
// stack: <byKey flag> <actors> <cid> <choice arg> <token>
Expand Down Expand Up @@ -1288,7 +1288,7 @@ private[lf] final case class Compiler(
}
SELet(coid) in
withLabel(
s"<fetch ${tmplId}>",
s"fetch @${tmplId.qualifiedName}",
SEAbs(1) {
SELet(
SBUFetch(tmplId)(
Expand Down Expand Up @@ -1337,7 +1337,7 @@ private[lf] final case class Compiler(

SELet(arg) in
withLabel(
s"<create ${tmplId}>",
s"create @${tmplId.qualifiedName}",
SEAbs(1) {
// We check precondition in a separated builtin to prevent
// further evaluation of agreement, signatories, observers and key
Expand Down Expand Up @@ -1404,7 +1404,7 @@ private[lf] final case class Compiler(
SELet(encodeKeyWithMaintainers(key, tmplKey)) in {
env = env.incrPos // key with maintainers
withLabel(
s"<exercise_by_key ${tmplId}:${choiceId}>",
s"exerciseByKey @${tmplId.qualifiedName} ${choiceId}",
SEAbs(1) {
env = env.incrPos // token
SELet(
Expand Down Expand Up @@ -1435,7 +1435,7 @@ private[lf] final case class Compiler(

withEnv { _ =>
withLabel(
s"<create_and_exercise ${tmplId}:${choiceId}>",
s"createAndExercise @${tmplId.qualifiedName} ${choiceId}",
SEAbs(1) {
env = env.incrPos // token
SELet(
Expand Down Expand Up @@ -1468,7 +1468,7 @@ private[lf] final case class Compiler(
SELet(encodeKeyWithMaintainers(key, templateKey)) in {
env = env.incrPos // keyWithM
withLabel(
s"<lookup_by_key ${templateId}>",
s"lookupByKey @${templateId.qualifiedName}",
SEAbs(1) {
env = env.incrPos // token
SELet(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,57 @@ object Profile {
import com.daml.lf.speedy.SExpr._
rawLabel match {
case null => "<null>" // NOTE(MH): We should never see this but it's no problem if we do.
case AnonymousClosure => "<anonymous closure>"
case LfDefRef(ref) => ref.toString()
case ChoiceDefRef(tmplRef, name) => s"<exercise ${tmplRef}:${name}>"
case ref: SEBuiltinRecursiveDefinition.Reference => s"<${ref.toString().toLowerCase()}>"
case AnonymousClosure => "<lambda>"
case LfDefRef(ref) => ref.qualifiedName.toString()
case ChoiceDefRef(tmplRef, name) => s"exercise @${tmplRef.qualifiedName} ${name}"
case ref: SEBuiltinRecursiveDefinition.Reference => ref.toString().toLowerCase()
case str: String => str
case any => s"<unknown ${any}>"
}
}
}

private def unmangleLenient(str: String): String = {
val builder = new StringBuilder(str.length)
var i = 0
while (i < str.length) {
if (str(i) == '$' && i + 1 < str.length) {
str(i + 1) match {
case '$' =>
builder.append('$')
i += 2
case 'u' if i + 5 < str.length =>
try {
val cp = Integer.parseUnsignedInt(str.substring(i + 2, i + 6), 16)
builder.appendAll(Character.toChars(cp))
i += 6
} catch {
case _: NumberFormatException =>
builder.append('$')
i += 1
}
case 'U' if i + 9 < str.length =>
try {
val cp = Integer.parseUnsignedInt(str.substring(i + 2, i + 10), 16)
builder.appendAll(Character.toChars(cp))
i += 10
} catch {
case _: NumberFormatException =>
builder.append('$')
i += 1
}
case _ =>
builder.append('$')
i += 1
}
} else {
builder.append(str(i))
i += 1
}
}
builder.toString
}

/** Utility object to convert the profile into the JSON format required by
* https://www.speedscope.app/. For a description of the format, see
* https://github.com/jlfwong/speedscope/wiki/Importing-from-custom-sources#speedscopes-file-format
Expand Down Expand Up @@ -111,7 +152,7 @@ object Profile {
case Some(index) => index
case None =>
val index = frames.size()
frames.add(FrameJson(label))
frames.add(FrameJson(unmangleLenient(label)))
frameIndices.put(label, index)
index
}
Expand Down

0 comments on commit 6d71475

Please sign in to comment.