Skip to content

Commit 94d5d9d

Browse files
author
Pavel Kosov
committed
Remove nm dependency, used objdump –t instead.
It is necessary to use the correct nm and objdump for some targets (for example ARM thumb). Wrong nm can provide an information but addresses may be wrong. The correct nm for ARM will report the address 0x1234 for a thumb binary, but system’s nm (x86) will report the address 0x1235. It is very hard to investigate such issues. We suggest to remove nm dependency at all and use objdump -t instead. Note objdump allows demangling too. It makes the profile page more human friendly. Here is the objdump -t output format for reference https://github.com/llvm-mirror/llvm/blob/2c4ca6832fa6b306ee6a7010bfb80a3f2596f824/tools/llvm-objdump/llvm-objdump.cpp#L1850 OS Laboratory. Huawei Russian Research Institute. Saint-Petersburg Reviewed By: tnfchris Differential Revision: https://reviews.llvm.org/D114641
1 parent 32c39b2 commit 94d5d9d

File tree

5 files changed

+81
-60
lines changed

5 files changed

+81
-60
lines changed

lnt/server/ui/profile_views.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from flask import render_template, current_app
66
import os
77
import json
8+
import urllib
89
from lnt.server.ui.decorators import v4_route, frontend
910
from lnt.server.ui.globals import v4_url_for
1011
from lnt.server.ui.views import ts_data
@@ -108,7 +109,7 @@ def v4_profile_ajax_getCodeForFunction():
108109
ts = request.get_testsuite()
109110
runid = request.args.get('runid')
110111
testid = request.args.get('testid')
111-
f = request.args.get('f')
112+
f = urllib.parse.unquote(request.args.get('f'))
112113

113114
profileDir = current_app.old_config.profileDir
114115

lnt/server/ui/static/lnt_profile.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -475,7 +475,7 @@ Profile.prototype = {
475475
$.ajax(g_urls.getCodeForFunction, {
476476
dataType: "json",
477477
data: {'runid': this.runid, 'testid': this.testid,
478-
'f': fname},
478+
'f': encodeURIComponent(fname)},
479479
success: function(data) {
480480
this_.data = data;
481481
this_._display();
@@ -1075,9 +1075,10 @@ function FunctionTypeahead(element, options) {
10751075
},
10761076
updater: function(item) {
10771077
// FIXME: the item isn't passed in as json any more, it's
1078-
// been rendered. Lame. Hack around this by splitting apart
1079-
// the ','-concatenated 2-tuple again.
1080-
fname = item.split(',')[0];
1078+
// been rendered ("fname,[object Object]"). Lame. Hack around
1079+
// this by splitting apart the ','-concatenated 2-tuple again.
1080+
var splitIndex = item.lastIndexOf(',');
1081+
fname = item.substr(0, splitIndex);
10811082

10821083
options.updated(fname);
10831084
return fname;

lnt/testing/profile/cPerf.cpp

Lines changed: 72 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
// if the mmap's event total is < 1% of the total in all counters,
3838
// then discard the mmap [1].
3939
//
40-
// load symbol data by calling "nm" and parsing the result.
40+
// load symbol data by calling "objdump -t" and parsing the result.
4141
// for all PCs we have events for, in sorted order:
4242
// find the symbol this PC is part of -> Sym
4343
// look up all PCs between Sym.Start and Sym.End and emit data
@@ -50,7 +50,7 @@
5050
// invoked, and its samples will continue until the perf wrapper tool exits.
5151
// This means that it will often take one or two samples in intermediate
5252
// binaries like "perf", "bash", or libaries such as libdl. We don't care about
53-
// these, and these binaries and libraries are very large to nm and objdump.
53+
// these, and these binaries and libraries are very large to objdump.
5454
//
5555
// So we have a threshold - if a binary contains < 1% of all samples, don't
5656
// bother importing it.
@@ -361,7 +361,7 @@ static const char* sw_event_names[PERF_COUNT_SW_MAX] = {
361361
};
362362

363363
//===----------------------------------------------------------------------===//
364-
// Readers for nm and objdump output
364+
// Readers for objdump output
365365
//===----------------------------------------------------------------------===//
366366

367367
struct Map {
@@ -380,19 +380,15 @@ struct Symbol {
380380
}
381381
};
382382

383-
class NmOutput : public std::vector<Symbol> {
383+
class SymTabOutput : public std::vector<Symbol> {
384384
public:
385-
std::string Nm, BinaryCacheRoot;
385+
std::string Objdump, BinaryCacheRoot;
386386

387-
NmOutput(std::string Nm, std::string BinaryCacheRoot)
388-
: Nm(Nm), BinaryCacheRoot(BinaryCacheRoot) {}
387+
SymTabOutput(std::string Objdump, std::string BinaryCacheRoot)
388+
: Objdump(Objdump), BinaryCacheRoot(BinaryCacheRoot) {}
389389

390-
void fetchSymbols(Map *M, bool Dynamic) {
391-
std::string D = "-D";
392-
if (!Dynamic)
393-
// Don't fetch the dynamic symbols - instead fetch static ones.
394-
D = "";
395-
std::string Cmd = Nm + " " + D + " -S --defined-only " +
390+
void fetchSymbols(Map *M) {
391+
std::string Cmd = Objdump + " -t -T -C " +
396392
BinaryCacheRoot + std::string(M->Filename) +
397393
#ifdef _WIN32
398394
" 2> NUL";
@@ -408,38 +404,65 @@ class NmOutput : public std::vector<Symbol> {
408404
if (Len == -1)
409405
break;
410406

411-
std::vector<std::string> SplittedLine;
412-
if (splitLine(std::string(Line), SplittedLine) < 4)
413-
continue;
407+
std::stringstream SS(Line);
414408

415-
const std::string& One = SplittedLine[0];
416-
const std::string& Two = SplittedLine[1];
417-
const std::string& Three = SplittedLine[2];
418-
std::string& Four = SplittedLine[3];
409+
std::string Start;
410+
if (!std::getline(SS, Start, ' '))
411+
continue;
412+
char* EndPtr = NULL;
413+
uint64_t NStart = strtoull(Start.c_str(), &EndPtr, 16);
414+
if (EndPtr == Start.c_str())
415+
continue;
419416

420-
char *EndPtr = NULL;
421-
uint64_t Start = strtoull(One.c_str(), &EndPtr, 16);
422-
if (EndPtr == One.c_str())
417+
char GlobLoc; // Local -> 'l', Global -> 'g', Neither -> ' '
418+
if (!SS.get(GlobLoc))
423419
continue;
424-
uint64_t Extent = strtoull(Two.c_str(), &EndPtr, 16);
425-
if (EndPtr == Two.c_str())
420+
char Weak; // Weak?
421+
if (!SS.get(Weak))
426422
continue;
427-
if (Three.length() != 1)
423+
char Space;
424+
if (!SS.get(Space)) // Constructor. Not supported yet.
428425
continue;
429-
switch (Three.front()) {
430-
default:
426+
if (!SS.get(Space)) // Warning. Not supported yet.
431427
continue;
432-
case 'T':
433-
case 't': // Text section
434-
case 'V':
435-
case 'v': // Weak object
436-
case 'W':
437-
case 'w': // Weak object (not tagged as such)
438-
break;
439-
}
440-
if (Four.back() == '\n')
441-
Four.pop_back();
442-
push_back({Start, Start + Extent, Four});
428+
char IFunc; // Indirect reference to another symbol.
429+
if (!SS.get(IFunc))
430+
continue;
431+
char Debug; // Debugging (d) or dynamic (D) symbol.
432+
if (!SS.get(Debug))
433+
continue;
434+
char FileFunc; // Name of function (F), file (f) or object (O).
435+
if (!SS.get(FileFunc))
436+
continue;
437+
if (FileFunc != 'F')
438+
continue;
439+
if (!SS.get(Space))
440+
continue;
441+
442+
std::string Section;
443+
if (!std::getline(SS, Section, '\t'))
444+
continue;
445+
if (Section != ".text")
446+
continue;
447+
448+
std::string Extent;
449+
if (!std::getline(SS, Extent, ' '))
450+
continue;
451+
uint64_t NExtent = strtoull(Extent.c_str(), &EndPtr, 16);
452+
if (EndPtr == Extent.c_str())
453+
continue;
454+
455+
std::string Func;
456+
while (std::getline(SS, Func, ' ') && Func.empty()); // Skip spaces.
457+
if (Func.empty())
458+
continue;
459+
// Note Func includes the symbol table visibility if any.
460+
std::string FuncRest;
461+
if (std::getline(SS, FuncRest)) // Read the rest line if any.
462+
Func += std::string(" ") + FuncRest;
463+
if (Func.back() == '\n')
464+
Func.pop_back();
465+
push_back({NStart, NStart + NExtent, Func});
443466
}
444467
if (Line)
445468
free(Line);
@@ -455,8 +478,7 @@ class NmOutput : public std::vector<Symbol> {
455478
void reset(Map *M) {
456479
clear();
457480
// Fetch both dynamic and static symbols, sort and unique them.
458-
fetchSymbols(M, true);
459-
fetchSymbols(M, false);
481+
fetchSymbols(M);
460482

461483
std::sort(begin(), end());
462484
auto NewEnd = std::unique(begin(), end());
@@ -568,8 +590,8 @@ class ObjdumpOutput {
568590

569591
class PerfReader {
570592
public:
571-
PerfReader(const std::string &Filename, std::string Nm,
572-
std::string Objdump, std::string BinaryCacheRoot);
593+
PerfReader(const std::string &Filename, std::string Objdump,
594+
std::string BinaryCacheRoot);
573595
~PerfReader();
574596

575597
void readHeader();
@@ -613,13 +635,12 @@ class PerfReader {
613635
PyObject *Functions, *TopLevelCounters;
614636
std::vector<PyObject*> Lines;
615637

616-
std::string Nm, Objdump, BinaryCacheRoot;
638+
std::string Objdump, BinaryCacheRoot;
617639
};
618640

619-
PerfReader::PerfReader(const std::string &Filename,
620-
std::string Nm, std::string Objdump,
641+
PerfReader::PerfReader(const std::string &Filename, std::string Objdump,
621642
std::string BinaryCacheRoot)
622-
: Nm(Nm), Objdump(Objdump), BinaryCacheRoot(BinaryCacheRoot) {
643+
: Objdump(Objdump), BinaryCacheRoot(BinaryCacheRoot) {
623644
TopLevelCounters = PyDict_New();
624645
Functions = PyDict_New();
625646
#ifdef _WIN32
@@ -934,7 +955,7 @@ void PerfReader::emitMaps() {
934955

935956
uint64_t Adjust = Maps[MapID].Adjust;
936957

937-
NmOutput Syms(Nm, BinaryCacheRoot);
958+
SymTabOutput Syms(Objdump, BinaryCacheRoot);
938959
Syms.reset(&Maps[MapID]);
939960

940961
// Accumulate the event totals for each symbol
@@ -1008,14 +1029,13 @@ PyObject *PerfReader::complete() {
10081029
#ifndef STANDALONE
10091030
static PyObject *cPerf_importPerf(PyObject *self, PyObject *args) {
10101031
const char *Fname;
1011-
const char *Nm = "nm";
10121032
const char *Objdump = "objdump";
10131033
const char *BinaryCacheRoot = "";
1014-
if (!PyArg_ParseTuple(args, "s|sss", &Fname, &Nm, &Objdump, &BinaryCacheRoot))
1034+
if (!PyArg_ParseTuple(args, "s|ss", &Fname, &Objdump, &BinaryCacheRoot))
10151035
return NULL;
10161036

10171037
try {
1018-
PerfReader P(Fname, Nm, Objdump, BinaryCacheRoot);
1038+
PerfReader P(Fname, Objdump, BinaryCacheRoot);
10191039
P.readHeader();
10201040
P.readAttrs();
10211041
P.readDataStream();
@@ -1066,7 +1086,7 @@ PyMODINIT_FUNC initcPerf(void) {
10661086
int main(int argc, char **argv) {
10671087
Py_Initialize();
10681088
if (argc < 2) return -1;
1069-
PerfReader P(argv[1], "nm", "objdump", "");
1089+
PerfReader P(argv[1], "objdump", "");
10701090
P.readHeader();
10711091
P.readAttrs();
10721092
P.readDataStream();

lnt/testing/profile/perf.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ def checkFile(fn):
2222
return f.read(8) == b'PERFILE2'
2323

2424
@staticmethod
25-
def deserialize(f, nm='nm', objdump='objdump', propagateExceptions=False,
25+
def deserialize(f, objdump='objdump', propagateExceptions=False,
2626
binaryCacheRoot=''):
2727
f = f.name
2828

@@ -31,7 +31,7 @@ def deserialize(f, nm='nm', objdump='objdump', propagateExceptions=False,
3131
return None
3232

3333
try:
34-
data = cPerf.importPerf(f, nm, objdump)
34+
data = cPerf.importPerf(f, objdump, binaryCacheRoot)
3535

3636
# Go through the data and convert counter values to percentages.
3737
for f in data['functions'].values():

lnt/testing/profile/profile.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ def fromFile(f):
3232
if impl is lnt.testing.profile.perf.LinuxPerfProfile:
3333
ret = impl.deserialize(
3434
fd,
35-
nm=os.getenv('CMAKE_NM', 'nm'),
3635
objdump=os.getenv('CMAKE_OBJDUMP', 'objdump'),
3736
binaryCacheRoot=os.getenv('LNT_BINARY_CACHE_ROOT', ''))
3837
else:

0 commit comments

Comments
 (0)