Smart client team Edden

background

Under the launching framework of the headline, the launching tasks have been clearly divided, and the launching timing is the key information in the launching tasks. Currently, the main way we get this information is by looking at Systrace, but there are some problems with reading systrace directly:

  • Systrace has some incomplete information under release, such as IO thread information, and the main evaluation scenario for launch optimization is release
  • Systrace information is relatively heavy, has poor readability, and interferes with the reading of the startup task

The above problems make it more difficult for developers to troubleshoot and verify startup task problems, and optimize startup tasks.

Therefore, this paper considers to design a lightweight information description, collection and information reconstruction scheme, which can flexibly adapt to release mode and debug mode, increase the readability and reduce the cost of troubleshooting for developers.

1 Scheme Design

The lightweight startup information building solution mainly consists of three parts:

  • Initiate information building: responsible for extracting key information into new data structures
  • Start information collection: Collect and output the information of each task to the rebuild module
  • Start information reconstruction: responsible for information construction, output visual graphics

2 concrete module implementation

2.1 Start information construction

data class InitDataStruct(
    var startTime: Long = 0,
    var duration: Long = 0,
    var currentProcess: String = "",
    var taskName: String = ""
)
Copy the code

The key startup information has the following dimensions:

  • Startup time (normalized)
  • Start the time-consuming
  • Starting a thread
  • Start the name

And don’t care about the tasks that need to be eliminated:

  • Non-launch task information (this is not to say that it is not important, it is just not superior in the launch framework area)
  • Start the stack task

The Format like

{"task_name":"class com.xxx.xxxTask","start_time":5,"duration":9,"current_process":"AA xxxThread#4"}
Copy the code

2.2 Starting information Collection

Since you are not connected to the company platform (too small), consider printing the results in log format.

I hope to achieve the following functions, but one by one is a little copy and paste a little too low

I looked into one AspectJ practice that you can use

@PointCut("execution(* com.xxx.xxx.xxxTask.run(*))")

Bury pointcuts around tasks

Use @before, @After injection to enter the code.

2.3 Starting information collection and Drawing

Since we currently rely on manual startup analysis, the way we collect startup information relies on logs printed by the Console, such as

{"task_name":"class com.xxx.Task","start_time":0,"duration":2,"current_process":"main"}
Copy the code

So let’s just write a reader tool and escape it, and make it a readable data structure

Def toInitInfo(json): return InitInfo(json["start_time"], json["duration"], json["current_process"], str(json["task_name"]).split('.')[-1]) class InitInfo: Def init__(self, startTime, duration, currentProcessName, taskName): self.startTime = startTime self.taskName = taskName self.duration = duration self.currentProcessName = currentProcessName def printitself(self): print("task_name : " + self.taskName) print("\tstartTime : " + str(self.startTime)) print("\tduration : " + str(self.duration)) print("\tcurrentProcessName : "+ self.currentProcessName) # Def getNameCombineDuration(self): Return self.taskname + "" + STR (self.duration) # return len(self.getNameCombineDuration()) + 2 def generateFormatStr(self, perTime, perBlank): TotalLen = Max (3, int(1.0 * perBlank * Max (1, self.duration)/perTime)) cntLen = Max (0, totalLen - self.getConstructLen()) strr = "|" + (cntLen / 2 + cntLen % 2) * "-" + self.getNameCombineDuration()[0:min(totalLen - 2, len(self.getNameCombineDuration()))]+ cntLen / 2 * "-" + "|" return strr def generateBlank(self, timeNow, perTime, perBlank): strr = max(0, int((self.startTime - timeNow) / perTime) * perBlank) * " " return strrCopy the code

And insert all tasks into the list with the completion time as the sort Function

def sortByEnd(initInfo1, initInfo2):
    return (initInfo1.startTime + initInfo1.duration) <= (initInfo2.startTime + initInfo2.duration)

def dealWithList():
    for item in line_jsons:
        if(taskMap.has_key(item.currentProcessName)):
            taskMap[item.currentProcessName].append(item)
        else:
            taskMap[item.currentProcessName] = []
            taskMap[item.currentProcessName].append(item)
Copy the code

Now to the heart of the matter, which rules we use to draw the graph, it depends on what information we need:

  • The first is to analyze the start task time. Systrace can be used, the horizontal axis is a fixed unit of time length, and the vertical axis is currentProcess
def drawMp(): DuraLen = 0 maxLen = 0 # 10ms interval currentPerTime = 10 endFile = open("timeline ","w" Taskmap. keys(): maxLen = Max (maxLen, len(key)) DuraLen = Max (duraLen, item.getConstructlen ()) # xplot = maxLen * "" +" :" for index in range(0, (line_jsons[-1].startTime + line_jsons[-1].duration) / currentPerTime): cntLen = duraLen - 2 - len(str(index * currentPerTime)) xplot += "|" + (cntLen / 2 + cntLen % 2) * "-" + str(index * CurrentPerTime) + cntLen / 2 * "-" + "|" endFile. Write (xplot + "\ n") # drawing for the key in taskMap. Keys () : strr = key + (maxLen - len(key)) * " " + " :" timeNow = 0 for item in taskMap[key]: item.printitself() strr += item.generateBlank(timeNow, perTime = currentPerTime, perBlank = duraLen) strr += item.generateFormatStr(10, duraLen) timeNow = item.startTime + item.duration strr += "\n" endFile.write(strr) endFile.close()Copy the code
  • The second is to analyze the rationality of the start task arrangement, that is, whether there is a long-tail start path. The horizontal axis is the start task time after discretization, and the vertical axis is currentProcess
DuraCordi = [] def mp2 (): # def addBlank(st, Ed): return (ed - st) * duraLen * " " def formatString(st, ed, taskName, duraLen): strr = "|" leftBlank = (ed - st) * duraLen - 2 - len(taskName) strr += (leftBlank / 2 + leftBlank % 2) * "-" strr += TaskName STRR + = leftBlank / 2 * "-" + "|" return STRR # discrete # is shortest first - > | maxLen (xxxTask) | dura = [] filee = open("timeline2.txt","w") for item in line_jsons: duraLen = max(duraLen, len(item.getNameCombineDuration()) + 2) dura.append(item.startTime) dura.append(item.startTime + item.duration) DuraCordi = list(set(dura)) duracordi.sort (): maxLen = max(maxLen, len(key)) for key in taskMap.keys(): currentIndex = 0 strr = key + (maxLen - len(key)) * " " + " :" for item in taskMap[key]: stIndex = bisect.bisect_left(duraCordi, item.startTime) edIndex = bisect.bisect_left(duraCordi, item.startTime + max(item.duration, 1)) strr += addBlank(currentIndex, stIndex) strr += formatString(stIndex, edIndex, item.getNameCombineDuration(), duraLen = duraLen) currentIndex = edIndex strr += "\n" filee.write(strr) filee.close()Copy the code

3 Comparison of effects

  • The first startup time is measured in units

  • The second startup time is discretized

For example, if we need to analyze whether the layout of startup tasks is reasonable, we can look at the second image. We can see that the main thread has many startup tasks, and there may be a certain long tail effect.

It’s much lighter than the Systrace