1*333d2b36SAndroid Build Coastguard Worker// Copyright 2024 Google Inc. All rights reserved. 2*333d2b36SAndroid Build Coastguard Worker// 3*333d2b36SAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License"); 4*333d2b36SAndroid Build Coastguard Worker// you may not use this file except in compliance with the License. 5*333d2b36SAndroid Build Coastguard Worker// You may obtain a copy of the License at 6*333d2b36SAndroid Build Coastguard Worker// 7*333d2b36SAndroid Build Coastguard Worker// http://www.apache.org/licenses/LICENSE-2.0 8*333d2b36SAndroid Build Coastguard Worker// 9*333d2b36SAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software 10*333d2b36SAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS, 11*333d2b36SAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*333d2b36SAndroid Build Coastguard Worker// See the License for the specific language governing permissions and 13*333d2b36SAndroid Build Coastguard Worker// limitations under the License. 14*333d2b36SAndroid Build Coastguard Worker 15*333d2b36SAndroid Build Coastguard Workerpackage elf 16*333d2b36SAndroid Build Coastguard Worker 17*333d2b36SAndroid Build Coastguard Workerimport ( 18*333d2b36SAndroid Build Coastguard Worker "io/fs" 19*333d2b36SAndroid Build Coastguard Worker "os" 20*333d2b36SAndroid Build Coastguard Worker "path/filepath" 21*333d2b36SAndroid Build Coastguard Worker "strings" 22*333d2b36SAndroid Build Coastguard Worker "sync" 23*333d2b36SAndroid Build Coastguard Worker "time" 24*333d2b36SAndroid Build Coastguard Worker) 25*333d2b36SAndroid Build Coastguard Worker 26*333d2b36SAndroid Build Coastguard Workerfunc UpdateBuildIdDir(path string) error { 27*333d2b36SAndroid Build Coastguard Worker path = filepath.Clean(path) 28*333d2b36SAndroid Build Coastguard Worker buildIdPath := path + "/.build-id" 29*333d2b36SAndroid Build Coastguard Worker 30*333d2b36SAndroid Build Coastguard Worker // Collect the list of files and build-id symlinks. If the symlinks are 31*333d2b36SAndroid Build Coastguard Worker // up to date (newer than the symbol files), there is nothing to do. 32*333d2b36SAndroid Build Coastguard Worker var buildIdFiles, symbolFiles []string 33*333d2b36SAndroid Build Coastguard Worker var buildIdMtime, symbolsMtime time.Time 34*333d2b36SAndroid Build Coastguard Worker filepath.WalkDir(path, func(path string, entry fs.DirEntry, err error) error { 35*333d2b36SAndroid Build Coastguard Worker if entry == nil || entry.IsDir() { 36*333d2b36SAndroid Build Coastguard Worker return nil 37*333d2b36SAndroid Build Coastguard Worker } 38*333d2b36SAndroid Build Coastguard Worker info, err := entry.Info() 39*333d2b36SAndroid Build Coastguard Worker if err != nil { 40*333d2b36SAndroid Build Coastguard Worker return err 41*333d2b36SAndroid Build Coastguard Worker } 42*333d2b36SAndroid Build Coastguard Worker mtime := info.ModTime() 43*333d2b36SAndroid Build Coastguard Worker if strings.HasPrefix(path, buildIdPath) { 44*333d2b36SAndroid Build Coastguard Worker if buildIdMtime.Compare(mtime) < 0 { 45*333d2b36SAndroid Build Coastguard Worker buildIdMtime = mtime 46*333d2b36SAndroid Build Coastguard Worker } 47*333d2b36SAndroid Build Coastguard Worker buildIdFiles = append(buildIdFiles, path) 48*333d2b36SAndroid Build Coastguard Worker } else { 49*333d2b36SAndroid Build Coastguard Worker if symbolsMtime.Compare(mtime) < 0 { 50*333d2b36SAndroid Build Coastguard Worker symbolsMtime = mtime 51*333d2b36SAndroid Build Coastguard Worker } 52*333d2b36SAndroid Build Coastguard Worker symbolFiles = append(symbolFiles, path) 53*333d2b36SAndroid Build Coastguard Worker } 54*333d2b36SAndroid Build Coastguard Worker return nil 55*333d2b36SAndroid Build Coastguard Worker }) 56*333d2b36SAndroid Build Coastguard Worker if symbolsMtime.Compare(buildIdMtime) < 0 { 57*333d2b36SAndroid Build Coastguard Worker return nil 58*333d2b36SAndroid Build Coastguard Worker } 59*333d2b36SAndroid Build Coastguard Worker 60*333d2b36SAndroid Build Coastguard Worker // Collect build-id -> file mapping from ELF files in the symbols directory. 61*333d2b36SAndroid Build Coastguard Worker concurrency := 8 62*333d2b36SAndroid Build Coastguard Worker done := make(chan error) 63*333d2b36SAndroid Build Coastguard Worker buildIdToFile := make(map[string]string) 64*333d2b36SAndroid Build Coastguard Worker var mu sync.Mutex 65*333d2b36SAndroid Build Coastguard Worker for i := 0; i != concurrency; i++ { 66*333d2b36SAndroid Build Coastguard Worker go func(paths []string) { 67*333d2b36SAndroid Build Coastguard Worker for _, path := range paths { 68*333d2b36SAndroid Build Coastguard Worker id, err := Identifier(path, true) 69*333d2b36SAndroid Build Coastguard Worker if err != nil { 70*333d2b36SAndroid Build Coastguard Worker done <- err 71*333d2b36SAndroid Build Coastguard Worker return 72*333d2b36SAndroid Build Coastguard Worker } 73*333d2b36SAndroid Build Coastguard Worker if id == "" { 74*333d2b36SAndroid Build Coastguard Worker continue 75*333d2b36SAndroid Build Coastguard Worker } 76*333d2b36SAndroid Build Coastguard Worker mu.Lock() 77*333d2b36SAndroid Build Coastguard Worker oldPath := buildIdToFile[id] 78*333d2b36SAndroid Build Coastguard Worker if oldPath == "" || oldPath > path { 79*333d2b36SAndroid Build Coastguard Worker buildIdToFile[id] = path 80*333d2b36SAndroid Build Coastguard Worker } 81*333d2b36SAndroid Build Coastguard Worker mu.Unlock() 82*333d2b36SAndroid Build Coastguard Worker } 83*333d2b36SAndroid Build Coastguard Worker done <- nil 84*333d2b36SAndroid Build Coastguard Worker }(symbolFiles[len(symbolFiles)*i/concurrency : len(symbolFiles)*(i+1)/concurrency]) 85*333d2b36SAndroid Build Coastguard Worker } 86*333d2b36SAndroid Build Coastguard Worker 87*333d2b36SAndroid Build Coastguard Worker // Collect previously generated build-id -> file mapping from the .build-id directory. 88*333d2b36SAndroid Build Coastguard Worker // We will use this for incremental updates. If we see anything in the .build-id 89*333d2b36SAndroid Build Coastguard Worker // directory that we did not expect, we'll delete it and start over. 90*333d2b36SAndroid Build Coastguard Worker prevBuildIdToFile := make(map[string]string) 91*333d2b36SAndroid Build Coastguard Workerout: 92*333d2b36SAndroid Build Coastguard Worker for _, buildIdFile := range buildIdFiles { 93*333d2b36SAndroid Build Coastguard Worker if !strings.HasSuffix(buildIdFile, ".debug") { 94*333d2b36SAndroid Build Coastguard Worker prevBuildIdToFile = nil 95*333d2b36SAndroid Build Coastguard Worker break 96*333d2b36SAndroid Build Coastguard Worker } 97*333d2b36SAndroid Build Coastguard Worker buildId := buildIdFile[len(buildIdPath)+1 : len(buildIdFile)-6] 98*333d2b36SAndroid Build Coastguard Worker for i, ch := range buildId { 99*333d2b36SAndroid Build Coastguard Worker if i == 2 { 100*333d2b36SAndroid Build Coastguard Worker if ch != '/' { 101*333d2b36SAndroid Build Coastguard Worker prevBuildIdToFile = nil 102*333d2b36SAndroid Build Coastguard Worker break out 103*333d2b36SAndroid Build Coastguard Worker } 104*333d2b36SAndroid Build Coastguard Worker } else { 105*333d2b36SAndroid Build Coastguard Worker if (ch < '0' || ch > '9') && (ch < 'a' || ch > 'f') { 106*333d2b36SAndroid Build Coastguard Worker prevBuildIdToFile = nil 107*333d2b36SAndroid Build Coastguard Worker break out 108*333d2b36SAndroid Build Coastguard Worker } 109*333d2b36SAndroid Build Coastguard Worker } 110*333d2b36SAndroid Build Coastguard Worker } 111*333d2b36SAndroid Build Coastguard Worker target, err := os.Readlink(buildIdFile) 112*333d2b36SAndroid Build Coastguard Worker if err != nil || !strings.HasPrefix(target, "../../") { 113*333d2b36SAndroid Build Coastguard Worker prevBuildIdToFile = nil 114*333d2b36SAndroid Build Coastguard Worker break 115*333d2b36SAndroid Build Coastguard Worker } 116*333d2b36SAndroid Build Coastguard Worker prevBuildIdToFile[buildId[0:2]+buildId[3:]] = path + target[5:] 117*333d2b36SAndroid Build Coastguard Worker } 118*333d2b36SAndroid Build Coastguard Worker if prevBuildIdToFile == nil { 119*333d2b36SAndroid Build Coastguard Worker err := os.RemoveAll(buildIdPath) 120*333d2b36SAndroid Build Coastguard Worker if err != nil { 121*333d2b36SAndroid Build Coastguard Worker return err 122*333d2b36SAndroid Build Coastguard Worker } 123*333d2b36SAndroid Build Coastguard Worker prevBuildIdToFile = make(map[string]string) 124*333d2b36SAndroid Build Coastguard Worker } 125*333d2b36SAndroid Build Coastguard Worker 126*333d2b36SAndroid Build Coastguard Worker // Wait for build-id collection from ELF files to finish. 127*333d2b36SAndroid Build Coastguard Worker for i := 0; i != concurrency; i++ { 128*333d2b36SAndroid Build Coastguard Worker err := <-done 129*333d2b36SAndroid Build Coastguard Worker if err != nil { 130*333d2b36SAndroid Build Coastguard Worker return err 131*333d2b36SAndroid Build Coastguard Worker } 132*333d2b36SAndroid Build Coastguard Worker } 133*333d2b36SAndroid Build Coastguard Worker 134*333d2b36SAndroid Build Coastguard Worker // Delete old symlinks. 135*333d2b36SAndroid Build Coastguard Worker for id, _ := range prevBuildIdToFile { 136*333d2b36SAndroid Build Coastguard Worker if buildIdToFile[id] == "" { 137*333d2b36SAndroid Build Coastguard Worker symlinkDir := buildIdPath + "/" + id[:2] 138*333d2b36SAndroid Build Coastguard Worker symlinkPath := symlinkDir + "/" + id[2:] + ".debug" 139*333d2b36SAndroid Build Coastguard Worker if err := os.Remove(symlinkPath); err != nil { 140*333d2b36SAndroid Build Coastguard Worker return err 141*333d2b36SAndroid Build Coastguard Worker } 142*333d2b36SAndroid Build Coastguard Worker } 143*333d2b36SAndroid Build Coastguard Worker } 144*333d2b36SAndroid Build Coastguard Worker 145*333d2b36SAndroid Build Coastguard Worker // Add new symlinks and update changed symlinks. 146*333d2b36SAndroid Build Coastguard Worker for id, path := range buildIdToFile { 147*333d2b36SAndroid Build Coastguard Worker prevPath := prevBuildIdToFile[id] 148*333d2b36SAndroid Build Coastguard Worker if prevPath == path { 149*333d2b36SAndroid Build Coastguard Worker continue 150*333d2b36SAndroid Build Coastguard Worker } 151*333d2b36SAndroid Build Coastguard Worker symlinkDir := buildIdPath + "/" + id[:2] 152*333d2b36SAndroid Build Coastguard Worker symlinkPath := symlinkDir + "/" + id[2:] + ".debug" 153*333d2b36SAndroid Build Coastguard Worker if prevPath == "" { 154*333d2b36SAndroid Build Coastguard Worker if err := os.MkdirAll(symlinkDir, 0755); err != nil { 155*333d2b36SAndroid Build Coastguard Worker return err 156*333d2b36SAndroid Build Coastguard Worker } 157*333d2b36SAndroid Build Coastguard Worker } else { 158*333d2b36SAndroid Build Coastguard Worker if err := os.Remove(symlinkPath); err != nil { 159*333d2b36SAndroid Build Coastguard Worker return err 160*333d2b36SAndroid Build Coastguard Worker } 161*333d2b36SAndroid Build Coastguard Worker } 162*333d2b36SAndroid Build Coastguard Worker 163*333d2b36SAndroid Build Coastguard Worker target, err := filepath.Rel(symlinkDir, path) 164*333d2b36SAndroid Build Coastguard Worker if err != nil { 165*333d2b36SAndroid Build Coastguard Worker return err 166*333d2b36SAndroid Build Coastguard Worker } 167*333d2b36SAndroid Build Coastguard Worker if err := os.Symlink(target, symlinkPath); err != nil { 168*333d2b36SAndroid Build Coastguard Worker return err 169*333d2b36SAndroid Build Coastguard Worker } 170*333d2b36SAndroid Build Coastguard Worker } 171*333d2b36SAndroid Build Coastguard Worker return nil 172*333d2b36SAndroid Build Coastguard Worker} 173